Skip to content

Commit

Permalink
Merge pull request #181 from clover/release/p4.1.2
Browse files Browse the repository at this point in the history
Release 4.1.2
  • Loading branch information
matt-willson-clover authored Nov 2, 2021
2 parents c33acd0 + 37a0242 commit ffd2c36
Show file tree
Hide file tree
Showing 13 changed files with 67 additions and 45 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## Version

Current version: 4.0.6
Current version: 4.1.2

## Platforms supported

Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "remote-pay-cloud",
"version": "4.0.6",
"version": "4.1.2",
"description": "Access Clover devices through the cloud.",
"keywords": [
"clover",
Expand Down Expand Up @@ -32,7 +32,7 @@
},
"dependencies": {
"eventemitter3": "4.0.7",
"remote-pay-cloud-api": "4.0.4"
"remote-pay-cloud-api": "4.0.5"
},
"devDependencies": {
"@types/node": "12.6.8",
Expand Down
2 changes: 1 addition & 1 deletion src/com/clover/Version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export class Version {
/**
* @type {string} - The current version of this library
*/
public static CLOVER_CLOUD_SDK_VERSION = "4.0.6";
public static CLOVER_CLOUD_SDK_VERSION = "4.1.2";

/**
* @type {string} - The current SDK name.
Expand Down
3 changes: 3 additions & 0 deletions src/com/clover/remote/client/CloverConnector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ export class CloverConnector implements sdk.remotepay.ICloverConnector {
if (request.getExternalReferenceId() != null) {
builder.setExternalReferenceId(request.getExternalReferenceId());
}
if (request.getPresentQrcOnly() != null) {
builder.setIsPresentQrcOnly(request.getPresentQrcOnly())
}

builder.setTransactionSettings(transactionSettings);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export class CloverWebSocketClient implements WebSocketListener {
return (this.socket ? this.socket.getBufferedAmount(): 0);
}

public connect(): void {
public connect(accessToken: string): void {
if (this.socket != null) {
throw new Error("Socket already created. Must create a new CloverWebSocketClient");
}
Expand All @@ -41,7 +41,7 @@ export class CloverWebSocketClient implements WebSocketListener {
this.socket = this.webSocketImplClass(this.endpoint);
// socket.setAutoFlush(true);
this.socket.addListener(this);
this.socket.connect();
this.socket.connect(accessToken);
} catch (e) {
this.logger.error('connect, connectionError', e);
this.listener.connectionError(this, e.message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,12 @@ export class WebSocketCloudCloverTransport extends WebSocketCloverTransport {
// We should only enter this block if the browser is IE 11. IE 11 has issues when the first call to the
// server is a POST (initializeWithServer). To work-around this we make a GET request
// See http://jonnyreeves.co.uk/2013/making-xhr-request-to-https-domains-with-winjs/ for more information.
this.httpSupport.getData(Endpoints.getMerchantEndpoint(this.cloverServer, this.merchantId, this.accessToken),
this.httpSupport.getData(Endpoints.getMerchantEndpoint(this.cloverServer, this.merchantId),
(_) => this.obtainWebSocketUrlAndSendPushAlert(),
(error) => {
this.logger.warn("IE 11 - Initial GET failed.", error);
});
},
this.buildAuthorizationHeader(this.accessToken));
} else {
// We aren't using IE, make the initial POST.
this.obtainWebSocketUrlAndSendPushAlert();
Expand All @@ -113,7 +114,7 @@ export class WebSocketCloudCloverTransport extends WebSocketCloverTransport {
// Do the notification call. This needs to happen every time we attempt to connect.
// It COULD mean that the device gets a notification when the Cloud Pay Display is
// already running, but this is not harmful.
let alertEndpoint: string = Endpoints.getAlertDeviceEndpoint(this.cloverServer, this.merchantId, this.accessToken);
let alertEndpoint: string = Endpoints.getAlertDeviceEndpoint(this.cloverServer, this.merchantId);
let deviceContactInfo: DeviceContactInfo = new DeviceContactInfo(this.deviceId.replace(/-/g, ""), true);
this.httpSupport.postData(alertEndpoint,
(data) => this.deviceNotificationSent(data),
Expand All @@ -124,7 +125,8 @@ export class WebSocketCloudCloverTransport extends WebSocketCloverTransport {
null,
error && error.status !== 404);
},
deviceContactInfo);
deviceContactInfo,
this.buildAuthorizationHeader(this.accessToken));
}

/**
Expand All @@ -141,7 +143,7 @@ export class WebSocketCloudCloverTransport extends WebSocketCloverTransport {
// we will assume an earlier version of the protocol on the server,
// and assume that the notification WAS SENT.
if (!notificationResponse.hasOwnProperty('sent') || notificationResponse.sent) {
this.webSocketURL = Endpoints.getDeviceWebSocketEndpoint(notificationResponse, this.friendlyId, this.forceConnect, this.merchantId, this.accessToken);
this.webSocketURL = Endpoints.getDeviceWebSocketEndpoint(notificationResponse, this.friendlyId, this.forceConnect, this.merchantId);
this.doOptionsCallToAvoid401Error(this.webSocketURL);
} else {
this.connectionError(this.cloverWebSocketClient, "The device is unreachable or an error has occurred sending the device a notification to start/connect to Cloud Pay Display.");
Expand Down Expand Up @@ -169,7 +171,8 @@ export class WebSocketCloudCloverTransport extends WebSocketCloverTransport {
}
this.httpSupport.options(httpUrl,
(data, xmlHttpReqImpl) => this.afterOptionsCall(deviceWebSocketEndpoint, xmlHttpReqImpl),
(data, xmlHttpReqImpl) => this.afterOptionsCall(deviceWebSocketEndpoint, xmlHttpReqImpl));
(data, xmlHttpReqImpl) => this.afterOptionsCall(deviceWebSocketEndpoint, xmlHttpReqImpl),
this.buildAuthorizationHeader(this.accessToken));
}

/**
Expand Down Expand Up @@ -208,7 +211,11 @@ export class WebSocketCloudCloverTransport extends WebSocketCloverTransport {
this.notifyConnectionAttemptComplete();
return; // done connecting
}
super.initializeWithUri(deviceWebSocketEndpoint);
super.initializeWithUri(deviceWebSocketEndpoint, this.accessToken);
}

private buildAuthorizationHeader(token: string): object {
return {"Authorization": `Bearer ${token}`};
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,9 @@ export abstract class WebSocketCloverTransport extends CloverTransport implement
* Called from subclasses at the end of the constructor.
*
* @param deviceEndpoint
* @param accessToken
*/
protected initializeWithUri(deviceEndpoint: string): void { // synchronized
protected initializeWithUri(deviceEndpoint: string, accessToken?: string): void { // synchronized
if (this.cloverWebSocketClient != null) {
if (this.cloverWebSocketClient.isOpen() || this.cloverWebSocketClient.isConnecting()) {
return;
Expand All @@ -136,7 +137,7 @@ export abstract class WebSocketCloverTransport extends CloverTransport implement
}
}
this.cloverWebSocketClient = new CloverWebSocketClient(deviceEndpoint, this, this.webSocketImplClass);
this.cloverWebSocketClient.connect();
this.cloverWebSocketClient.connect(accessToken);
this.logger.info('Connection attempt complete.');
this.notifyConnectionAttemptComplete();
}
Expand Down
20 changes: 8 additions & 12 deletions src/com/clover/util/Endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,15 @@ export class Endpoints {
* The endpoint used to connect to a websocket on the server that will proxy to a device. Used by
* remote-pay cloud connectors.
*
* @param {any} - notificationResponse - The notification response from COS.
* @param {any} notificationResponse - The notification response from COS.
* @param {string} friendlyId - an id used to identify the POS.
* @param {boolean} forceConnect - if true, then the attempt will overtake any existing connection
* @param {boolean} merchantId - unique identifier for the merchant.
* @param {boolean} accessToken - mid access token
* @returns {string} The endpoint used to connect to a websocket on the server that will proxy to a device
*/
public static getDeviceWebSocketEndpoint(notificationResponse: any, friendlyId: string, forceConnect: boolean, merchantId: string, accessToken: string): string {
public static getDeviceWebSocketEndpoint(notificationResponse: any, friendlyId: string, forceConnect: boolean, merchantId: string): string {
const variables = {};
// This is not an access token its a device id.
variables[Endpoints.WEBSOCKET_TOKEN_KEY] = notificationResponse.token;
variables[Endpoints.DOMAIN_KEY] = notificationResponse.host;
variables[Endpoints.WEBSOCKET_FRIENDLY_ID_KEY] = encodeURIComponent(friendlyId);
Expand All @@ -121,16 +121,14 @@ export class Endpoints {
*
* @param {string} domain - the clover server. EX: https://www.clover.com, http://localhost:9000
* @param {string} merchantId - the id of the merchant to use when getting the merchant.
* @param {string} accessToken - the OAuth token used when accessing the server
* @returns {string} endpoint - the url to use to retrieve the merchant
*/
public static getMerchantEndpoint(domain: string, merchantId: string, accessToken: string): string {
public static getMerchantEndpoint(domain: string, merchantId: string): string {
var variables = {};
variables[Endpoints.MERCHANT_V3_KEY] = merchantId;
variables[Endpoints.ACCESS_TOKEN_KEY] = accessToken;
variables[Endpoints.DOMAIN_KEY] = domain;

let merchantEndpointPath: string = Endpoints.DOMAIN_PATH + Endpoints.MERCHANT_V3_PATH + Endpoints.ACCESS_TOKEN_SUFFIX;
let merchantEndpointPath: string = Endpoints.DOMAIN_PATH + Endpoints.MERCHANT_V3_PATH;
return Endpoints.setVariables(merchantEndpointPath, variables);
}

Expand All @@ -146,10 +144,9 @@ export class Endpoints {
public static getDevicesEndpoint(domain: string, merchantId: string, accessToken: string): string {
var variables = {};
variables[Endpoints.MERCHANT_V3_KEY] = merchantId;
variables[Endpoints.ACCESS_TOKEN_KEY] = accessToken;
variables[Endpoints.DOMAIN_KEY] = domain;

let devicesEndpointPath: string = Endpoints.DOMAIN_PATH + Endpoints.DEVICE_PATH + Endpoints.ACCESS_TOKEN_SUFFIX;
let devicesEndpointPath: string = Endpoints.DOMAIN_PATH + Endpoints.DEVICE_PATH;
return Endpoints.setVariables(devicesEndpointPath, variables);
}

Expand All @@ -160,13 +157,12 @@ export class Endpoints {
* @param {string} accessToken - the OAuth token used when accessing the server
* @returns {string} endpoint - the url to use alert a device that we want to communicate with it
*/
public static getAlertDeviceEndpoint(domain: string, merchantId: string, accessToken: string): string {
public static getAlertDeviceEndpoint(domain: string, merchantId: string): string {
var variables = {};
variables[Endpoints.MERCHANT_V3_KEY] = merchantId;
variables[Endpoints.ACCESS_TOKEN_KEY] = accessToken;
variables[Endpoints.DOMAIN_KEY] = domain;

let alertDeviceEndpointPath: string = Endpoints.DOMAIN_PATH + Endpoints.REMOTE_PAY_PATH + Endpoints.ACCESS_TOKEN_SUFFIX;
let alertDeviceEndpointPath: string = Endpoints.DOMAIN_PATH + Endpoints.REMOTE_PAY_PATH;
return Endpoints.setVariables(alertDeviceEndpointPath, variables);
}

Expand Down
21 changes: 8 additions & 13 deletions src/com/clover/util/HttpSupport.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {Logger} from '../remote/client/util/Logger';
import {addHeaders} from "./addHeaders";

/**
* Interface used to abstract implementation details to allow for NodeJS and
Expand Down Expand Up @@ -68,7 +69,7 @@ export class HttpSupport {
/**
* Make the REST call to get the data
*/
public doXmlHttp(method: string, endpoint: string, onDataLoaded: Function, onError: Function): void {
public doXmlHttp(method: string, endpoint: string, onDataLoaded: Function, onError: Function, additionalHeaders?: any): void {
const xmlHttp = new this.xmlHttpImplClass();
this.setXmlHttpCallback(xmlHttp, endpoint, onDataLoaded, onError);
xmlHttp.open(method, endpoint, true);
Expand All @@ -77,7 +78,7 @@ export class HttpSupport {
if (typeof(navigator) !== "undefined" && navigator.userAgent.search("Firefox")) {
xmlHttp.setRequestHeader("Accept", "*/*");
}

addHeaders(additionalHeaders, xmlHttp);
xmlHttp.send();
}

Expand All @@ -86,13 +87,7 @@ export class HttpSupport {
this.setXmlHttpCallback(xmlHttp, endpoint, onDataLoaded, onError);

xmlHttp.open(method, endpoint, true);
if (additionalHeaders) {
for (var key in additionalHeaders) {
if (additionalHeaders.hasOwnProperty(key)) {
xmlHttp.setRequestHeader(key, additionalHeaders[key]);
}
}
}
addHeaders(additionalHeaders, xmlHttp);
if (sendData) {
xmlHttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
var sendDataStr = JSON.stringify(sendData);
Expand All @@ -113,15 +108,15 @@ export class HttpSupport {
/**
* Make the REST call to get the data
*/
public getData(endpoint: string, onDataLoaded: Function, onError: Function): void {
this.doXmlHttp("GET", endpoint, onDataLoaded, onError)
public getData(endpoint: string, onDataLoaded: Function, onError: Function, additionalHeaders?: object): void {
this.doXmlHttp("GET", endpoint, onDataLoaded, onError, additionalHeaders)
}

/**
* Make the REST call to get the data
*/
public options(endpoint: string, onDataLoaded: Function, onError: Function): void {
this.doXmlHttp("OPTIONS", endpoint, onDataLoaded, onError)
public options(endpoint: string, onDataLoaded: Function, onError: Function, additionalHeaders?: object): void {
this.doXmlHttp("OPTIONS", endpoint, onDataLoaded, onError, additionalHeaders)
}

/**
Expand Down
8 changes: 8 additions & 0 deletions src/com/clover/util/PayIntent/Builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export namespace PayIntent {
private transactionSettings: sdk.payments.TransactionSettings;
private passThroughValues: object;
private externalReferenceId: string;
private isPresentQrcOnly: boolean;

public static buildTransactionSettingsFromPayIntent(payIntent: sdk.remotemessage.PayIntent): sdk.payments.TransactionSettings {
let transactionSettings: sdk.payments.TransactionSettings = new sdk.payments.TransactionSettings();
Expand Down Expand Up @@ -310,6 +311,11 @@ export namespace PayIntent {
return this;
}

public setIsPresentQrcOnly(isPresentQrcOnly: boolean): Builder {
this.isPresentQrcOnly = isPresentQrcOnly;
return this;
}

public build(): sdk.remotemessage.PayIntent {
let payIntent: sdk.remotemessage.PayIntent = new sdk.remotemessage.PayIntent();
payIntent.setAction(this.action);
Expand Down Expand Up @@ -353,6 +359,8 @@ export namespace PayIntent {

payIntent.setPassThroughValues(this.passThroughValues);
payIntent.setExternalReferenceId(this.externalReferenceId);
payIntent.setIsPresentQrcOnly(this.isPresentQrcOnly);

return payIntent;
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/com/clover/util/addHeaders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const addHeaders = (headers, xmlHttp) => {
if (headers) {
for (let key in headers) {
if (headers.hasOwnProperty(key)) {
xmlHttp.setRequestHeader(key, headers[key]);
}
}
}
}
6 changes: 4 additions & 2 deletions src/com/clover/websocket/BrowserWebSocketImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ export class BrowserWebSocketImpl extends CloverWebSocketInterface {
*
* @override
* @param endpoint - the url that will connected to
* @param accessToken - Here the access token is passed as a second param to `new WebSocket()` and will be read
* by the support server as a "subprotocol" in the Sec-WebSocket-Protocol header value.
* @returns {WebSocket} - the specific implementation of a websocket
*/
public createWebSocket(endpoint: string): any {
return new WebSocket(endpoint);
public createWebSocket(endpoint: string, accessToken?: string): any {
return new WebSocket(endpoint, accessToken);
}

/**
Expand Down
7 changes: 4 additions & 3 deletions src/com/clover/websocket/CloverWebSocketInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,12 @@ export abstract class CloverWebSocketInterface {
* will not be properly attached before events begin firing.
*
* @param endpoint - the uri to connect to
* @param accessToken - the access token that is used to ensure the merchant and device are associated to the token
*/
public abstract createWebSocket(endpoint: string): any;
public abstract createWebSocket(endpoint: string, accessToken): any;

public connect(): CloverWebSocketInterface {
this.webSocket = this.createWebSocket(this.endpoint);
public connect(accessToken: string): CloverWebSocketInterface {
this.webSocket = this.createWebSocket(this.endpoint, accessToken);
if (typeof this.webSocket["addEventListener"] !== "function") {
this.logger.error("FATAL: The websocket implementation being used must have an 'addEventListener' function. Either use a supported websocket implementation (https://www.npmjs.com/package/ws) or override the connect method on CloverWebSocketInterface.");
} else {
Expand Down

0 comments on commit ffd2c36

Please sign in to comment.