From 196e7d26897f7c74774f29b8c9dd836be4f3777a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Mon, 12 Aug 2024 18:00:48 +0900 Subject: [PATCH 01/24] released --- packages/identity/package-lock.json | 4 ++-- packages/identity/package.json | 2 +- packages/payment/package-lock.json | 4 ++-- packages/payment/package.json | 2 +- packages/terminal/package-lock.json | 4 ++-- packages/terminal/package.json | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/identity/package-lock.json b/packages/identity/package-lock.json index c3325aba..9cacd84b 100644 --- a/packages/identity/package-lock.json +++ b/packages/identity/package-lock.json @@ -1,12 +1,12 @@ { "name": "@capacitor-community/stripe-identity", - "version": "6.0.2", + "version": "6.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@capacitor-community/stripe-identity", - "version": "6.0.2", + "version": "6.1.0", "license": "MIT", "dependencies": { "@stripe/stripe-js": "^2.1.11" diff --git a/packages/identity/package.json b/packages/identity/package.json index 5f17bcc8..56964dbf 100644 --- a/packages/identity/package.json +++ b/packages/identity/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor-community/stripe-identity", - "version": "6.0.2", + "version": "6.1.0", "engines": { "node": ">=18.0.0" }, diff --git a/packages/payment/package-lock.json b/packages/payment/package-lock.json index c998bdaf..5614e28f 100644 --- a/packages/payment/package-lock.json +++ b/packages/payment/package-lock.json @@ -1,12 +1,12 @@ { "name": "@capacitor-community/stripe", - "version": "6.0.2", + "version": "6.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@capacitor-community/stripe", - "version": "6.0.2", + "version": "6.1.0", "license": "MIT", "devDependencies": { "@capacitor/android": "^6.0.0", diff --git a/packages/payment/package.json b/packages/payment/package.json index 55ec8f2f..8feeac06 100644 --- a/packages/payment/package.json +++ b/packages/payment/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor-community/stripe", - "version": "6.0.2", + "version": "6.1.0", "engines": { "node": ">=18.0.0" }, diff --git a/packages/terminal/package-lock.json b/packages/terminal/package-lock.json index 79236838..0a3479da 100644 --- a/packages/terminal/package-lock.json +++ b/packages/terminal/package-lock.json @@ -1,12 +1,12 @@ { "name": "@capacitor-community/stripe-terminal", - "version": "6.0.2", + "version": "6.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@capacitor-community/stripe-terminal", - "version": "6.0.2", + "version": "6.1.0", "license": "MIT", "devDependencies": { "@capacitor/android": "^6.0.0", diff --git a/packages/terminal/package.json b/packages/terminal/package.json index 22494ab9..cc530418 100644 --- a/packages/terminal/package.json +++ b/packages/terminal/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor-community/stripe-terminal", - "version": "6.0.2", + "version": "6.1.0", "engines": { "node": ">=18.0.0" }, From 4c617e50ed8a4255fa326a3a7d0173fa20b911d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Mon, 12 Aug 2024 18:55:04 +0900 Subject: [PATCH 02/24] feat(terminal): create method of listed --- .../stripe/terminal/StripeTerminal.java | 130 ++++++++++++++++++ .../stripe/terminal/StripeTerminalPlugin.java | 25 ++++ .../terminal/ios/Plugin/StripeTerminal.swift | 83 ++++++++++- .../ios/Plugin/StripeTerminalPlugin.m | 5 + .../ios/Plugin/StripeTerminalPlugin.swift | 21 +++ .../terminal/ios/Plugin/TerminalMappers.swift | 30 ++++ packages/terminal/src/definitions.ts | 11 ++ packages/terminal/src/web.ts | 17 +++ 8 files changed, 321 insertions(+), 1 deletion(-) diff --git a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java index a0252d00..5754b139 100644 --- a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java +++ b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java @@ -28,6 +28,8 @@ import com.stripe.stripeterminal.external.callable.TerminalListener; import com.stripe.stripeterminal.external.models.BatteryStatus; import com.stripe.stripeterminal.external.models.CardPresentDetails; +import com.stripe.stripeterminal.external.models.Cart; +import com.stripe.stripeterminal.external.models.CartLineItem; import com.stripe.stripeterminal.external.models.CollectConfiguration; import com.stripe.stripeterminal.external.models.ConnectionConfiguration.BluetoothConnectionConfiguration; import com.stripe.stripeterminal.external.models.ConnectionConfiguration.InternetConnectionConfiguration; @@ -54,12 +56,15 @@ import java.util.List; import java.util.Objects; import org.jetbrains.annotations.NotNull; +import org.json.JSONException; +import org.json.JSONObject; public class StripeTerminal extends Executor { private TokenProvider tokenProvider; private Cancelable discoveryCancelable; private Cancelable collectCancelable; + private Cancelable installUpdateCancelable; private List readers; private String locationId; private PluginCall collectCall; @@ -440,6 +445,130 @@ public void confirmPaymentIntent(final PluginCall call) { Terminal.getInstance().confirmPaymentIntent(this.paymentIntentInstance, confirmPaymentMethodCallback); } + public void installAvailableUpdate(final PluginCall call) { + Terminal.getInstance().installAvailableUpdate(); + call.resolve(emptyObject); + } + + public void cancelInstallUpdate(final PluginCall call) { + if (this.installUpdateCancelable == null || this.installUpdateCancelable.isCompleted()) { + call.resolve(); + return; + } + + this.installUpdateCancelable.cancel( + new Callback() { + @Override + public void onSuccess() { + installUpdateCancelable = null; + call.resolve(); + } + + @Override + public void onFailure(@NonNull TerminalException e) { + call.reject(e.getLocalizedMessage()); + } + } + ); + } + + public void setReaderDisplay(final PluginCall call) { + String currency = call.getString("currency", null); + if (currency == null) { + call.reject("You must provide a currency value"); + return; + } + + int tax = call.getInt("tax", 0); + int total = call.getInt("total", 0); + if (total == 0) { + call.reject("You must provide a total value"); + return; + } + + JSArray lineItems = call.getArray("lineItems"); + List lineItemsList; + try { + lineItemsList = lineItems.toList(); + } catch (JSONException e) { + call.reject(e.getLocalizedMessage()); + return; + } + + List cartLineItems = new ArrayList<>(); + for (JSONObject item : lineItemsList) { + try { + JSObject itemObj = JSObject.fromJSONObject(item); + cartLineItems.add( + new CartLineItem( + Objects.requireNonNull(itemObj.getString("displayName")), + Objects.requireNonNull(itemObj.getInteger("quantity")), + Objects.requireNonNull(itemObj.getInteger("amount")) + ) + ); + } catch (JSONException e) { + call.reject(e.getLocalizedMessage()); + return; + } + } + + Cart cart = new Cart.Builder(currency, tax, total, cartLineItems).build(); + + Terminal + .getInstance() + .setReaderDisplay( + cart, + new Callback() { + @Override + public void onSuccess() { + call.resolve(); + } + + @Override + public void onFailure(@NonNull TerminalException e) { + call.reject(e.getErrorMessage()); + } + } + ); + } + + public void clearReaderDisplay(final PluginCall call) { + Terminal + .getInstance() + .clearReaderDisplay( + new Callback() { + @Override + public void onSuccess() { + call.resolve(); + } + + @Override + public void onFailure(@NonNull TerminalException e) { + call.reject(e.getErrorMessage()); + } + } + ); + } + + public void rebootReader(final PluginCall call) { + Terminal + .getInstance() + .rebootReader( + new Callback() { + @Override + public void onSuccess() { + paymentIntentInstance = null; + call.resolve(emptyObject); + } + + @Override + public void onFailure(@NonNull TerminalException e) { + call.reject(e.getLocalizedMessage()); + } + } + ); + } + private final PaymentIntentCallback confirmPaymentMethodCallback = new PaymentIntentCallback() { @Override public void onSuccess(PaymentIntent paymentIntent) { @@ -476,6 +605,7 @@ private ReaderListener readerListener() { @Override public void onStartInstallingUpdate(@NotNull ReaderSoftwareUpdate update, @NotNull Cancelable cancelable) { // Show UI communicating that a required update has started installing + installUpdateCancelable = cancelable; notifyListeners( TerminalEnumEvent.StartInstallingUpdate.getWebEventName(), new JSObject().put("update", convertReaderSoftwareUpdate(update)) diff --git a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminalPlugin.java b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminalPlugin.java index e52cd7a9..3d9bc698 100644 --- a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminalPlugin.java +++ b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminalPlugin.java @@ -160,4 +160,29 @@ public void cancelCollectPaymentMethod(final PluginCall call) { public void confirmPaymentIntent(PluginCall call) { this.implementation.confirmPaymentIntent(call); } + + @PluginMethod + public void installAvailableUpdate(PluginCall call) { + this.implementation.installAvailableUpdate(call); + } + + @PluginMethod + public void cancelInstallUpdate(PluginCall call) { + this.implementation.cancelInstallUpdate(call); + } + + @PluginMethod + public void setReaderDisplay(PluginCall call) { + this.implementation.setReaderDisplay(call); + } + + @PluginMethod + public void clearReaderDisplay(PluginCall call) { + this.implementation.clearReaderDisplay(call); + } + + @PluginMethod + public void rebootReader(PluginCall call) { + this.implementation.rebootReader(call); + } } diff --git a/packages/terminal/ios/Plugin/StripeTerminal.swift b/packages/terminal/ios/Plugin/StripeTerminal.swift index 86aa9e69..35683ec6 100644 --- a/packages/terminal/ios/Plugin/StripeTerminal.swift +++ b/packages/terminal/ios/Plugin/StripeTerminal.swift @@ -6,11 +6,14 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg weak var plugin: StripeTerminalPlugin? private let apiClient = APIClient() + var discoverCancelable: Cancelable? + var collectCancelable: Cancelable? + var installUpdateCancelable: Cancelable? + var discoverCall: CAPPluginCall? var locationId: String? var isTest: Bool? - var collectCancelable: Cancelable? var type: DiscoveryMethod? var isInitialize: Bool = false var paymentIntent: PaymentIntent? @@ -250,6 +253,82 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg call.resolve([:]) } + public func installAvailableUpdate(_ call: CAPPluginCall) { + Terminal.shared.installAvailableUpdate() + call.resolve([:]) + } + + public func cancelInstallUpdate(_ call: CAPPluginCall) { + self.installUpdateCancelable?.cancel { error in + if let error = error as NSError? { + call.reject(error.localizedDescription) + } else { + call.resolve([:]) + } + } + } + + public func setReaderDisplay(_ call: CAPPluginCall) { + guard let currency = call.getString("currency") else { + call.reject("You must provide a currency value") + return + } + guard let tax = call.getInt("tax") as? NSNumber else { + call.reject("You must provide a tax value") + return + } + guard let total = call.getInt("total") as? NSNumber else { + call.reject("You must provide a total value") + return + } + + let cartBuilder = CartBuilder(currency: currency) + .setTax(Int(truncating: tax)) + .setTotal(Int(truncating: total)) + + let cartLineItems = TerminalMappers.mapToCartLineItems(call.getArray("lineItems") ?? JSArray()) + + cartBuilder.setLineItems(cartLineItems) + + let cart: Cart + do { + cart = try cartBuilder.build() + } catch { + call.reject(error.localizedDescription) + return + } + + Terminal.shared.setReaderDisplay(cart) { error in + if let error = error as NSError? { + call.reject(error.localizedDescription) + } else { + call.resolve([:]) + } + } + + } + + public func clearReaderDisplay(_ call: CAPPluginCall) { + Terminal.shared.clearReaderDisplay { error in + if let error = error as NSError? { + call.reject(error.localizedDescription) + } else { + call.resolve([:]) + } + } + } + + public func rebootReader(_ call: CAPPluginCall) { + Terminal.shared.rebootReader { error in + if let error = error as NSError? { + call.reject(error.localizedDescription) + } else { + self.paymentIntent = nil + call.resolve([:]) + } + } + } + /* * Terminal */ @@ -270,6 +349,7 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg */ public func localMobileReader(_ reader: Reader, didStartInstallingUpdate update: ReaderSoftwareUpdate, cancelable: Cancelable?) { + self.installUpdateCancelable = cancelable self.plugin?.notifyListeners(TerminalEvents.StartInstallingUpdate.rawValue, data: self.convertReaderSoftwareUpdate(update: update)) } @@ -311,6 +391,7 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg } public func reader(_: Reader, didStartInstallingUpdate update: ReaderSoftwareUpdate, cancelable: Cancelable?) { + self.installUpdateCancelable = cancelable self.plugin?.notifyListeners(TerminalEvents.StartInstallingUpdate.rawValue, data: self.convertReaderSoftwareUpdate(update: update)) } diff --git a/packages/terminal/ios/Plugin/StripeTerminalPlugin.m b/packages/terminal/ios/Plugin/StripeTerminalPlugin.m index cd0a8876..c0a9e7ee 100644 --- a/packages/terminal/ios/Plugin/StripeTerminalPlugin.m +++ b/packages/terminal/ios/Plugin/StripeTerminalPlugin.m @@ -15,4 +15,9 @@ CAP_PLUGIN_METHOD(cancelCollectPaymentMethod, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(confirmPaymentIntent, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(setSimulatorConfiguration, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(installAvailableUpdate, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(cancelInstallUpdate, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(setReaderDisplay, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(clearReaderDisplay, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(rebootReader, CAPPluginReturnPromise); ) diff --git a/packages/terminal/ios/Plugin/StripeTerminalPlugin.swift b/packages/terminal/ios/Plugin/StripeTerminalPlugin.swift index 32822010..8ab1a30f 100644 --- a/packages/terminal/ios/Plugin/StripeTerminalPlugin.swift +++ b/packages/terminal/ios/Plugin/StripeTerminalPlugin.swift @@ -64,4 +64,25 @@ public class StripeTerminalPlugin: CAPPlugin { @objc func confirmPaymentIntent(_ call: CAPPluginCall) { self.implementation.confirmPaymentIntent(call) } + + @objc func installAvailableUpdate(_ call: CAPPluginCall) { + self.implementation.installAvailableUpdate(call) + } + + @objc func cancelInstallUpdate(_ call: CAPPluginCall) { + self.implementation.cancelInstallUpdate(call) + } + + @objc func setReaderDisplay(_ call: CAPPluginCall) { + self.implementation.setReaderDisplay(call) + } + + @objc func clearReaderDisplay(_ call: CAPPluginCall) { + self.implementation.clearReaderDisplay(call) + } + + @objc func rebootReader(_ call: CAPPluginCall) { + self.implementation.rebootReader(call) + } + } diff --git a/packages/terminal/ios/Plugin/TerminalMappers.swift b/packages/terminal/ios/Plugin/TerminalMappers.swift index a63ed32e..0ab43deb 100644 --- a/packages/terminal/ios/Plugin/TerminalMappers.swift +++ b/packages/terminal/ios/Plugin/TerminalMappers.swift @@ -2,6 +2,36 @@ import StripeTerminal import Capacitor class TerminalMappers { + class func mapToCartLineItem(_ cartLineItem: NSDictionary) -> CartLineItem? { + guard let displayName = cartLineItem["displayName"] as? String else { return nil } + guard let quantity = cartLineItem["quantity"] as? NSNumber else { return nil } + guard let amount = cartLineItem["amount"] as? NSNumber else { return nil } + + do { + let lineItem = try CartLineItemBuilder(displayName: displayName) + .setQuantity(Int(truncating: quantity)) + .setAmount(Int(truncating: amount)) + .build() + return lineItem + } catch { + print("Error wihle building CartLineItem, error:\(error)") + return nil + } + } + + class func mapToCartLineItems(_ cartLineItems: JSArray) -> [CartLineItem] { + var items = [CartLineItem]() + + cartLineItems.forEach { + if let item = $0 as? NSDictionary { + if let lineItem = TerminalMappers.mapToCartLineItem(item) { + items.append(lineItem) + } + } + } + return items + } + class func mapToSimulateReaderUpdate(_ update: String) -> SimulateReaderUpdate { switch update { case "UPDATE_AVAILABLE": return SimulateReaderUpdate.available diff --git a/packages/terminal/src/definitions.ts b/packages/terminal/src/definitions.ts index 69567941..649844ec 100644 --- a/packages/terminal/src/definitions.ts +++ b/packages/terminal/src/definitions.ts @@ -27,6 +27,12 @@ export type ReaderSoftwareUpdateInterface = { timeEstimate: UpdateTimeEstimate; }; +export type CartLineItem = { + displayName: string; + quantity: number; + amount: number; +}; + export * from './events.enum'; export * from './stripe.enum'; export interface StripeTerminalPlugin { @@ -56,6 +62,11 @@ export interface StripeTerminalPlugin { collectPaymentMethod(options: { paymentIntent: string }): Promise; cancelCollectPaymentMethod(): Promise; confirmPaymentIntent(): Promise; + installAvailableUpdate(): Promise; + cancelInstallUpdate(): Promise; + setReaderDisplay(lineItems: CartLineItem[]): Promise; + clearReaderDisplay(): Promise; + rebootReader(): Promise; addListener( eventName: TerminalEventsEnum.Loaded, diff --git a/packages/terminal/src/web.ts b/packages/terminal/src/web.ts index d4ba0fff..171313b4 100644 --- a/packages/terminal/src/web.ts +++ b/packages/terminal/src/web.ts @@ -6,6 +6,7 @@ import type { ReaderInterface, SimulateReaderUpdate, SimulatedCardType, + CartLineItem, } from './definitions'; import { TerminalEventsEnum } from './events.enum'; @@ -86,6 +87,22 @@ export class StripeTerminalWeb this.notifyListeners(TerminalEventsEnum.ConfirmedPaymentIntent, null); } + async installAvailableUpdate(): Promise { + console.log('installAvailableUpdate'); + } + async cancelInstallUpdate(): Promise { + console.log('cancelInstallUpdate'); + } + async setReaderDisplay(lineItems: CartLineItem[]): Promise { + console.log('setReaderDisplay', lineItems); + } + async clearReaderDisplay(): Promise { + console.log('clearReaderDisplay'); + } + async rebootReader(): Promise { + console.log('rebootReader'); + } + collect = 'deprecated'; cancelCollect = 'deprecated'; } From de2457bf4c9bdfb58d1fa7ada119dcaa3e532e91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Mon, 12 Aug 2024 18:58:38 +0900 Subject: [PATCH 03/24] chore(terminal): update docs --- packages/terminal/README.md | 59 +++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/packages/terminal/README.md b/packages/terminal/README.md index 29466bb5..f52cd50a 100644 --- a/packages/terminal/README.md +++ b/packages/terminal/README.md @@ -124,6 +124,11 @@ And update minSdkVersion to 26 And compileSdkVersion to 34 in your `android/app/ * [`collectPaymentMethod(...)`](#collectpaymentmethod) * [`cancelCollectPaymentMethod()`](#cancelcollectpaymentmethod) * [`confirmPaymentIntent()`](#confirmpaymentintent) +* [`installAvailableUpdate()`](#installavailableupdate) +* [`cancelInstallUpdate()`](#cancelinstallupdate) +* [`setReaderDisplay(...)`](#setreaderdisplay) +* [`clearReaderDisplay()`](#clearreaderdisplay) +* [`rebootReader()`](#rebootreader) * [`addListener(TerminalEventsEnum.Loaded, ...)`](#addlistenerterminaleventsenumloaded) * [`addListener(TerminalEventsEnum.RequestedConnectionToken, ...)`](#addlistenerterminaleventsenumrequestedconnectiontoken) * [`addListener(TerminalEventsEnum.DiscoveredReaders, ...)`](#addlistenerterminaleventsenumdiscoveredreaders) @@ -282,6 +287,55 @@ confirmPaymentIntent() => Promise -------------------- +### installAvailableUpdate() + +```typescript +installAvailableUpdate() => Promise +``` + +-------------------- + + +### cancelInstallUpdate() + +```typescript +cancelInstallUpdate() => Promise +``` + +-------------------- + + +### setReaderDisplay(...) + +```typescript +setReaderDisplay(lineItems: CartLineItem[]) => Promise +``` + +| Param | Type | +| --------------- | --------------------------- | +| **`lineItems`** | CartLineItem[] | + +-------------------- + + +### clearReaderDisplay() + +```typescript +clearReaderDisplay() => Promise +``` + +-------------------- + + +### rebootReader() + +```typescript +rebootReader() => Promise +``` + +-------------------- + + ### addListener(TerminalEventsEnum.Loaded, ...) ```typescript @@ -718,6 +772,11 @@ addListener(eventName: TerminalEventsEnum.PaymentStatusChange, listenerFunc: ({ { index: number; serialNumber: string; } +#### CartLineItem + +{ displayName: string; quantity: number; amount: number; } + + #### ReaderSoftwareUpdateInterface { version: string; settingsVersion: string; requiredAt: number; timeEstimate: UpdateTimeEstimate; } From 4d9f0c5cb7d9849e8eaa4383768be178bf05c8a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Mon, 12 Aug 2024 19:28:18 +0900 Subject: [PATCH 04/24] feat(terminal): add reconnection functions --- .../stripe/terminal/StripeTerminal.java | 34 +++++++++++-------- .../stripe/terminal/TerminalEvent.kt | 2 ++ .../terminal/ios/Plugin/StripeTerminal.swift | 34 ++++++++++++++++--- .../terminal/ios/Plugin/TerminalEvents.swift | 2 ++ packages/terminal/src/definitions.ts | 19 ++++++++++- packages/terminal/src/events.enum.ts | 2 ++ 6 files changed, 73 insertions(+), 20 deletions(-) diff --git a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java index 5754b139..529b422e 100644 --- a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java +++ b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java @@ -279,31 +279,31 @@ private void connectLocalMobileReader(final PluginCall call) { return; } + Boolean autoReconnectOnUnexpectedDisconnect = call.getBoolean("autoReconnectOnUnexpectedDisconnect", false); + LocalMobileConnectionConfiguration config = new LocalMobileConnectionConfiguration( this.locationId, - true, - this.localMobileReaderReconnectionListener + autoReconnectOnUnexpectedDisconnect, + this.readerReconnectionListener ); Terminal.getInstance().connectLocalMobileReader(this.readers.get(reader.getInteger("index")), config, this.readerCallback(call)); } - ReaderReconnectionListener localMobileReaderReconnectionListener = new ReaderReconnectionListener() { - @Override - public void onReaderReconnectStarted(@NonNull Reader reader, @NonNull Cancelable cancelReconnect) { - // 1. Notified at the start of a reconnection attempt - // Use cancelable to stop reconnection at any time - } - + ReaderReconnectionListener readerReconnectionListener = new ReaderReconnectionListener() { @Override public void onReaderReconnectSucceeded(@NonNull Reader reader) { - // 2. Notified when reader reconnection succeeds - // App is now connected + notifyListeners( + TerminalEnumEvent.ReaderReconnectSucceeded.getWebEventName(), + new JSObject().put("reader", convertReaderInterface(reader)) + ); } @Override public void onReaderReconnectFailed(@NonNull Reader reader) { - // 3. Notified when reader reconnection fails - // App is now disconnected + notifyListeners( + TerminalEnumEvent.ReaderReconnectFailed.getWebEventName(), + new JSObject().put("reader", convertReaderInterface(reader)) + ); } }; @@ -323,7 +323,13 @@ private void connectUsbReader(final PluginCall call) { private void connectBluetoothReader(final PluginCall call) { JSObject reader = call.getObject("reader"); - BluetoothConnectionConfiguration config = new BluetoothConnectionConfiguration(this.locationId); + Boolean autoReconnectOnUnexpectedDisconnect = call.getBoolean("autoReconnectOnUnexpectedDisconnect", false); + + BluetoothConnectionConfiguration config = new BluetoothConnectionConfiguration( + this.locationId, + autoReconnectOnUnexpectedDisconnect, + this.readerReconnectionListener + ); Terminal .getInstance() .connectBluetoothReader(this.readers.get(reader.getInteger("index")), config, this.readerListener(), this.readerCallback(call)); diff --git a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/TerminalEvent.kt b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/TerminalEvent.kt index 4bbf8460..5bc083c3 100644 --- a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/TerminalEvent.kt +++ b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/TerminalEvent.kt @@ -22,4 +22,6 @@ enum class TerminalEnumEvent(val webEventName: String) { RequestDisplayMessage("terminalRequestDisplayMessage"), RequestReaderInput("terminalRequestReaderInput"), PaymentStatusChange("terminalPaymentStatusChange"), + ReaderReconnectSucceeded("terminalReaderReconnectSucceeded"), + ReaderReconnectFailed("terminalReaderReconnectFailed"), } diff --git a/packages/terminal/ios/Plugin/StripeTerminal.swift b/packages/terminal/ios/Plugin/StripeTerminal.swift index 35683ec6..0be64e51 100644 --- a/packages/terminal/ios/Plugin/StripeTerminal.swift +++ b/packages/terminal/ios/Plugin/StripeTerminal.swift @@ -2,7 +2,7 @@ import Foundation import Capacitor import StripeTerminal -public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDelegate, BluetoothReaderDelegate, TerminalDelegate { +public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDelegate, BluetoothReaderDelegate, TerminalDelegate, ReconnectionDelegate { weak var plugin: StripeTerminalPlugin? private let apiClient = APIClient() @@ -143,10 +143,15 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg } private func connectLocalMobileReader(_ call: CAPPluginCall) { - let connectionConfig = try! LocalMobileConnectionConfigurationBuilder.init(locationId: self.locationId!).build() let reader: JSObject = call.getObject("reader")! + let autoReconnectOnUnexpectedDisconnect = call.getBool("autoReconnectOnUnexpectedDisconnect", false) let index: Int = reader["index"] as! Int + let connectionConfig = try! LocalMobileConnectionConfigurationBuilder.init(locationId: self.locationId!) + .setAutoReconnectOnUnexpectedDisconnect(autoReconnectOnUnexpectedDisconnect) + .setAutoReconnectionDelegate(autoReconnectOnUnexpectedDisconnect ? self : nil) + .build() + Terminal.shared.connectLocalMobileReader(self.readers![index], delegate: self, connectionConfig: connectionConfig) { reader, error in if let reader = reader { self.plugin?.notifyListeners(TerminalEvents.ConnectedReader.rawValue, data: [:]) @@ -158,11 +163,12 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg } private func connectInternetReader(_ call: CAPPluginCall) { + let reader: JSObject = call.getObject("reader")! + let index: Int = reader["index"] as! Int + let config = try! InternetConnectionConfigurationBuilder() .setFailIfInUse(true) .build() - let reader: JSObject = call.getObject("reader")! - let index: Int = reader["index"] as! Int Terminal.shared.connectInternetReader(self.readers![index], connectionConfig: config) { reader, error in if let reader = reader { @@ -175,9 +181,13 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg } private func connectBluetoothReader(_ call: CAPPluginCall) { - let config = try! BluetoothConnectionConfigurationBuilder(locationId: self.locationId!).build() let reader: JSObject = call.getObject("reader")! let index: Int = reader["index"] as! Int + let autoReconnectOnUnexpectedDisconnect = call.getBool("autoReconnectOnUnexpectedDisconnect", false) + let config = try! BluetoothConnectionConfigurationBuilder(locationId: self.locationId!) + .setAutoReconnectOnUnexpectedDisconnect(autoReconnectOnUnexpectedDisconnect) + .setAutoReconnectionDelegate(autoReconnectOnUnexpectedDisconnect ? self : nil) + .build() Terminal.shared.connectBluetoothReader(self.readers![index], delegate: self, connectionConfig: config) { reader, error in if let reader = reader { @@ -444,6 +454,20 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg ]) } + /* + * Reconnection + */ + public func readerDidSucceedReconnect(_ reader: Reader) { + self.plugin?.notifyListeners(TerminalEvents.ReaderReconnectSucceeded.rawValue, data: ["reader": self.convertReaderInterface(reader: reader)]) + } + + public func readerDidFailReconnect(_ reader: Reader) { + self.plugin?.notifyListeners(TerminalEvents.ReaderReconnectFailed.rawValue, data: ["reader": self.convertReaderInterface(reader: reader)]) + } + + /* + * Private + */ private func convertReaderInterface(reader: Reader) -> [String: String] { return ["serialNumber": reader.serialNumber] } diff --git a/packages/terminal/ios/Plugin/TerminalEvents.swift b/packages/terminal/ios/Plugin/TerminalEvents.swift index 2450762c..2cf0554b 100644 --- a/packages/terminal/ios/Plugin/TerminalEvents.swift +++ b/packages/terminal/ios/Plugin/TerminalEvents.swift @@ -19,4 +19,6 @@ public enum TerminalEvents: String { case RequestDisplayMessage = "terminalRequestDisplayMessage" case RequestReaderInput = "terminalRequestReaderInput" case PaymentStatusChange = "terminalPaymentStatusChange" + case ReaderReconnectSucceeded = "terminalReaderReconnectSucceeded" + case ReaderReconnectFailed = "terminalReaderReconnectFailed" } diff --git a/packages/terminal/src/definitions.ts b/packages/terminal/src/definitions.ts index 649844ec..77530048 100644 --- a/packages/terminal/src/definitions.ts +++ b/packages/terminal/src/definitions.ts @@ -55,7 +55,14 @@ export interface StripeTerminalPlugin { simulatedCard?: SimulatedCardType; simulatedTipAmount?: number; }): Promise; - connectReader(options: { reader: ReaderInterface }): Promise; + + /** + * @param options.autoReconnectOnUnexpectedDisconnect If true, the SDK will automatically attempt to reconnect to the reader. default is false. + */ + connectReader(options: { + reader: ReaderInterface; + autoReconnectOnUnexpectedDisconnect: boolean; + }): Promise; getConnectedReader(): Promise<{ reader: ReaderInterface | null }>; disconnectReader(): Promise; cancelDiscoverReaders(): Promise; @@ -316,6 +323,16 @@ export interface StripeTerminalPlugin { listenerFunc: ({ status }: { status: PaymentStatus }) => void, ): Promise; + addListener( + eventName: TerminalEventsEnum.ReaderReconnectSucceeded, + listenerFunc: ({ reader }: { reader: ReaderInterface }) => void, + ): Promise; + + addListener( + eventName: TerminalEventsEnum.ReaderReconnectFailed, + listenerFunc: ({ reader }: { reader: ReaderInterface }) => void, + ): Promise; + /** * @deprecated * This method has been deprecated and replaced by the `collectPaymentMethod`. diff --git a/packages/terminal/src/events.enum.ts b/packages/terminal/src/events.enum.ts index 54d32a19..07e9223d 100644 --- a/packages/terminal/src/events.enum.ts +++ b/packages/terminal/src/events.enum.ts @@ -20,6 +20,8 @@ export enum TerminalEventsEnum { RequestDisplayMessage = 'terminalRequestDisplayMessage', RequestReaderInput = 'terminalRequestReaderInput', PaymentStatusChange = 'terminalPaymentStatusChange', + ReaderReconnectSucceeded = 'terminalReaderReconnectSucceeded', + ReaderReconnectFailed = 'terminalReaderReconnectFailed', } export type TerminalResultInterface = From 44728aa1bbd3e443e3083209e83cf8c7201a1748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Mon, 12 Aug 2024 19:32:12 +0900 Subject: [PATCH 05/24] chore(terminal): autoReconnectOnUnexpectedDisconnect be option --- packages/terminal/src/definitions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/terminal/src/definitions.ts b/packages/terminal/src/definitions.ts index 77530048..ecb35927 100644 --- a/packages/terminal/src/definitions.ts +++ b/packages/terminal/src/definitions.ts @@ -61,7 +61,7 @@ export interface StripeTerminalPlugin { */ connectReader(options: { reader: ReaderInterface; - autoReconnectOnUnexpectedDisconnect: boolean; + autoReconnectOnUnexpectedDisconnect?: boolean; }): Promise; getConnectedReader(): Promise<{ reader: ReaderInterface | null }>; disconnectReader(): Promise; From b63ef5ec421ead90b0b98bb63e6a42d04d67ae59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Mon, 12 Aug 2024 20:16:24 +0900 Subject: [PATCH 06/24] fix(terminal): fix type Cart --- packages/terminal/README.md | 57 ++++++++++++++++++++++++---- packages/terminal/src/definitions.ts | 9 ++++- packages/terminal/src/web.ts | 8 ++-- 3 files changed, 61 insertions(+), 13 deletions(-) diff --git a/packages/terminal/README.md b/packages/terminal/README.md index f52cd50a..d6f2a857 100644 --- a/packages/terminal/README.md +++ b/packages/terminal/README.md @@ -149,6 +149,8 @@ And update minSdkVersion to 26 And compileSdkVersion to 34 in your `android/app/ * [`addListener(TerminalEventsEnum.RequestDisplayMessage, ...)`](#addlistenerterminaleventsenumrequestdisplaymessage) * [`addListener(TerminalEventsEnum.RequestReaderInput, ...)`](#addlistenerterminaleventsenumrequestreaderinput) * [`addListener(TerminalEventsEnum.PaymentStatusChange, ...)`](#addlistenerterminaleventsenumpaymentstatuschange) +* [`addListener(TerminalEventsEnum.ReaderReconnectSucceeded, ...)`](#addlistenerterminaleventsenumreaderreconnectsucceeded) +* [`addListener(TerminalEventsEnum.ReaderReconnectFailed, ...)`](#addlistenerterminaleventsenumreaderreconnectfailed) * [Interfaces](#interfaces) * [Type Aliases](#type-aliases) * [Enums](#enums) @@ -217,12 +219,12 @@ setSimulatorConfiguration(options: { update?: SimulateReaderUpdate; simulatedCar ### connectReader(...) ```typescript -connectReader(options: { reader: ReaderInterface; }) => Promise +connectReader(options: { reader: ReaderInterface; autoReconnectOnUnexpectedDisconnect?: boolean; }) => Promise ``` -| Param | Type | -| ------------- | ------------------------------------------------------------------------ | -| **`options`** | { reader: ReaderInterface; } | +| Param | Type | +| ------------- | ----------------------------------------------------------------------------------------------------------------------- | +| **`options`** | { reader: ReaderInterface; autoReconnectOnUnexpectedDisconnect?: boolean; } | -------------------- @@ -308,12 +310,12 @@ cancelInstallUpdate() => Promise ### setReaderDisplay(...) ```typescript -setReaderDisplay(lineItems: CartLineItem[]) => Promise +setReaderDisplay(options: Cart) => Promise ``` -| Param | Type | -| --------------- | --------------------------- | -| **`lineItems`** | CartLineItem[] | +| Param | Type | +| ------------- | ------------------------------------- | +| **`options`** | Cart | -------------------- @@ -754,6 +756,38 @@ addListener(eventName: TerminalEventsEnum.PaymentStatusChange, listenerFunc: ({ -------------------- +### addListener(TerminalEventsEnum.ReaderReconnectSucceeded, ...) + +```typescript +addListener(eventName: TerminalEventsEnum.ReaderReconnectSucceeded, listenerFunc: ({ reader }: { reader: ReaderInterface; }) => void) => Promise +``` + +| Param | Type | +| ------------------ | ------------------------------------------------------------------------------------------------- | +| **`eventName`** | TerminalEventsEnum.ReaderReconnectSucceeded | +| **`listenerFunc`** | ({ reader }: { reader: ReaderInterface; }) => void | + +**Returns:** Promise<PluginListenerHandle> + +-------------------- + + +### addListener(TerminalEventsEnum.ReaderReconnectFailed, ...) + +```typescript +addListener(eventName: TerminalEventsEnum.ReaderReconnectFailed, listenerFunc: ({ reader }: { reader: ReaderInterface; }) => void) => Promise +``` + +| Param | Type | +| ------------------ | ------------------------------------------------------------------------------------------------- | +| **`eventName`** | TerminalEventsEnum.ReaderReconnectFailed | +| **`listenerFunc`** | ({ reader }: { reader: ReaderInterface; }) => void | + +**Returns:** Promise<PluginListenerHandle> + +-------------------- + + ### Interfaces @@ -772,6 +806,11 @@ addListener(eventName: TerminalEventsEnum.PaymentStatusChange, listenerFunc: ({ { index: number; serialNumber: string; } +#### Cart + +{ currency: string; tax: number; total: number lineItems: CartLineItem[]; } + + #### CartLineItem { displayName: string; quantity: number; amount: number; } @@ -867,6 +906,8 @@ addListener(eventName: TerminalEventsEnum.PaymentStatusChange, listenerFunc: ({ | **`RequestDisplayMessage`** | 'terminalRequestDisplayMessage' | | **`RequestReaderInput`** | 'terminalRequestReaderInput' | | **`PaymentStatusChange`** | 'terminalPaymentStatusChange' | +| **`ReaderReconnectSucceeded`** | 'terminalReaderReconnectSucceeded' | +| **`ReaderReconnectFailed`** | 'terminalReaderReconnectFailed' | #### DisconnectReason diff --git a/packages/terminal/src/definitions.ts b/packages/terminal/src/definitions.ts index ecb35927..cfa9c86f 100644 --- a/packages/terminal/src/definitions.ts +++ b/packages/terminal/src/definitions.ts @@ -33,6 +33,13 @@ export type CartLineItem = { amount: number; }; +export type Cart = { + currency: string; + tax: number; + total: number + lineItems: CartLineItem[]; +}; + export * from './events.enum'; export * from './stripe.enum'; export interface StripeTerminalPlugin { @@ -71,7 +78,7 @@ export interface StripeTerminalPlugin { confirmPaymentIntent(): Promise; installAvailableUpdate(): Promise; cancelInstallUpdate(): Promise; - setReaderDisplay(lineItems: CartLineItem[]): Promise; + setReaderDisplay(options: Cart): Promise; clearReaderDisplay(): Promise; rebootReader(): Promise; diff --git a/packages/terminal/src/web.ts b/packages/terminal/src/web.ts index 171313b4..0dd14c83 100644 --- a/packages/terminal/src/web.ts +++ b/packages/terminal/src/web.ts @@ -1,12 +1,12 @@ import { WebPlugin } from '@capacitor/core'; -import type { +import { StripeTerminalPlugin, TerminalConnectTypes, ReaderInterface, SimulateReaderUpdate, SimulatedCardType, - CartLineItem, + Cart, } from './definitions'; import { TerminalEventsEnum } from './events.enum'; @@ -93,8 +93,8 @@ export class StripeTerminalWeb async cancelInstallUpdate(): Promise { console.log('cancelInstallUpdate'); } - async setReaderDisplay(lineItems: CartLineItem[]): Promise { - console.log('setReaderDisplay', lineItems); + async setReaderDisplay(options: Cart): Promise { + console.log('setReaderDisplay', options); } async clearReaderDisplay(): Promise { console.log('clearReaderDisplay'); From 48b32632d9bfb65dfb5cd2ed9e6a056a6d212df6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Mon, 12 Aug 2024 20:41:48 +0900 Subject: [PATCH 07/24] feat(terminal): added demo --- .../src/app/terminal/terminal.page.html | 4 +- .../angular/src/app/terminal/terminal.page.ts | 137 +++++++++++++++--- .../app/terminal/updateDeviceRequiredItems.ts | 54 +++++++ ...iceItems.ts => updateDeviceUpdateItems.ts} | 31 ++-- 4 files changed, 187 insertions(+), 39 deletions(-) create mode 100644 demo/angular/src/app/terminal/updateDeviceRequiredItems.ts rename demo/angular/src/app/terminal/{checkUpdateDeviceItems.ts => updateDeviceUpdateItems.ts} (85%) diff --git a/demo/angular/src/app/terminal/terminal.page.html b/demo/angular/src/app/terminal/terminal.page.html index c656f9be..8b60598c 100644 --- a/demo/angular/src/app/terminal/terminal.page.html +++ b/demo/angular/src/app/terminal/terminal.page.html @@ -37,13 +37,13 @@ Device Update - Happy Path simulateReaderUpdate.UpdateAvailable simulateReaderUpdate.Required setTimeout(resolve, 2000)); + + await StripeTerminal.clearReaderDisplay(); + } + await StripeTerminal.collectPaymentMethod({ paymentIntent }) .then(() => this.helper.updateItem(this.eventItems, 'collectPaymentMethod', true), @@ -186,28 +207,102 @@ export class TerminalPage { this.listenerHandlers.forEach((handler) => handler.remove()); } - async checkUpdateDevice(readerType: TerminalConnectTypes = TerminalConnectTypes.Bluetooth, simulateReaderUpdate: SimulateReaderUpdate) { - await this.prepareTerminalEvents(structuredClone(checkUpdateDeviceItems)); - - switch (simulateReaderUpdate) { - case SimulateReaderUpdate.UpdateAvailable: - await StripeTerminal.setSimulatorConfiguration({ update: SimulateReaderUpdate.UpdateAvailable }) - .then(() => this.helper.updateItem(this.eventItems, 'setSimulatorConfiguration:UPDATE_AVAILABLE', true)); - break; - case SimulateReaderUpdate.LowBattery: - await StripeTerminal.setSimulatorConfiguration({ update: SimulateReaderUpdate.LowBattery }) - .then(() => this.helper.updateItem(this.eventItems, 'setSimulatorConfiguration:LOW_BATTERY', true)); - break; - case SimulateReaderUpdate.LowBatterySucceedConnect: - await StripeTerminal.setSimulatorConfiguration({ update: SimulateReaderUpdate.LowBatterySucceedConnect }) - .then(() => this.helper.updateItem(this.eventItems, 'setSimulatorConfiguration:LOW_BATTERY_SUCCEED_CONNECT', true)); - break; - case SimulateReaderUpdate.Required: - await StripeTerminal.setSimulatorConfiguration({ update: SimulateReaderUpdate.Required }) - .then(() => this.helper.updateItem(this.eventItems, 'setSimulatorConfiguration:REQUIRED', true)); - break; + async checkUpdateDeviceUpdate(readerType: TerminalConnectTypes = TerminalConnectTypes.Bluetooth) { + await this.prepareTerminalEvents(structuredClone(updateDeviceUpdateItems)); + await StripeTerminal.setSimulatorConfiguration({ update: SimulateReaderUpdate.UpdateAvailable }) + .then(() => this.helper.updateItem(this.eventItems, 'setSimulatorConfiguration:UPDATE_AVAILABLE', true)); + + const result = await StripeTerminal.discoverReaders({ + type: readerType, + locationId: + [TerminalConnectTypes.Usb].includes(readerType) + ? 'tml_Ff37mAmk1XdBYT' // Auckland, New Zealand + : 'tml_FOUOdQVIxvVdvN', // San Francisco, CA 94110 + }).catch((e) => { + this.helper.updateItem(this.eventItems, 'discoverReaders', false); + throw e; + }); + + await this.helper.updateItem( + this.eventItems, + 'discoverReaders', + result.readers.length > 0, + ); + + const selectedReader = + result.readers.length === 1 + ? result.readers[0] + : await this.alertFilterReaders(result.readers); + console.log(selectedReader); + if (!selectedReader) { + alert('No reader selected'); + return; } + await StripeTerminal.connectReader({ + reader: selectedReader, + }).catch((e) => { + alert(e); + this.helper.updateItem(this.eventItems, 'connectReader', false); + throw e; + }); + await this.helper.updateItem(this.eventItems, 'connectReader', true); + + await StripeTerminal.installAvailableUpdate() + .then(() => this.helper.updateItem(this.eventItems, 'installAvailableUpdate', true)); + + await new Promise((resolve) => setTimeout(resolve, 1000)); + + await StripeTerminal.cancelInstallUpdate() + .then(() => this.helper.updateItem(this.eventItems, 'cancelInstallUpdate', true)); + + // await new Promise((resolve) => setTimeout(resolve, 5000)); + + const { paymentIntent } = await firstValueFrom( + this.http.post<{ + paymentIntent: string; + }>(environment.api + 'connection/intent', {}), + ).catch(async (e) => { + await this.helper.updateItem( + this.eventItems, + 'HttpClientPaymentIntent', + false, + ); + throw e; + }); + await this.helper.updateItem( + this.eventItems, + 'HttpClientPaymentIntent', + true, + ); + + await StripeTerminal.collectPaymentMethod({ paymentIntent }) + .then(() => + this.helper.updateItem(this.eventItems, 'collectPaymentMethod', true), + ) + .catch(async (e) => { + await this.helper.updateItem( + this.eventItems, + 'collectPaymentMethod', + false, + ); + throw e; + }); + + await StripeTerminal.disconnectReader().catch((e) => { + this.helper.updateItem(this.eventItems, 'disconnectReader', false); + throw e; + }); + await this.helper.updateItem(this.eventItems, 'disconnectReader', true); + + this.listenerHandlers.forEach((handler) => handler.remove()); + } + + async checkUpdateDeviceRequired(readerType: TerminalConnectTypes = TerminalConnectTypes.Bluetooth) { + await this.prepareTerminalEvents(structuredClone(updateDeviceRequiredItems)); + await StripeTerminal.setSimulatorConfiguration({ update: SimulateReaderUpdate.Required }) + .then(() => this.helper.updateItem(this.eventItems, 'setSimulatorConfiguration:REQUIRED', true)); + const result = await StripeTerminal.discoverReaders({ type: readerType, locationId: diff --git a/demo/angular/src/app/terminal/updateDeviceRequiredItems.ts b/demo/angular/src/app/terminal/updateDeviceRequiredItems.ts new file mode 100644 index 00000000..d379a682 --- /dev/null +++ b/demo/angular/src/app/terminal/updateDeviceRequiredItems.ts @@ -0,0 +1,54 @@ +import {ITestItems} from '../shared/interfaces'; +import {TerminalEventsEnum} from '@capacitor-community/stripe-terminal'; + +export const updateDeviceRequiredItems: ITestItems[] = [ + { + type: 'method', + name: 'initialize', + }, + { + type: 'event', + name: TerminalEventsEnum.Loaded, + }, + + { + type: 'method', + name: 'setSimulatorConfiguration:REQUIRED', + }, + { + type: 'method', + name: 'discoverReaders', + }, + { + type: 'event', + name: TerminalEventsEnum.DiscoveredReaders, + }, + { + type: 'method', + name: 'connectReader', + }, + { + type: 'event', + name: TerminalEventsEnum.ConnectedReader, + }, + { + type: 'event', + name: TerminalEventsEnum.StartInstallingUpdate, + }, + { + type: 'event', + name: TerminalEventsEnum.ReaderSoftwareUpdateProgress, + }, + { + type: 'event', + name: TerminalEventsEnum.FinishInstallingUpdate, + }, + { + type: 'method', + name: 'disconnectReader', + }, + { + type: 'event', + name: TerminalEventsEnum.DisconnectedReader, + }, +]; diff --git a/demo/angular/src/app/terminal/checkUpdateDeviceItems.ts b/demo/angular/src/app/terminal/updateDeviceUpdateItems.ts similarity index 85% rename from demo/angular/src/app/terminal/checkUpdateDeviceItems.ts rename to demo/angular/src/app/terminal/updateDeviceUpdateItems.ts index c6d9a3c7..51252cc3 100644 --- a/demo/angular/src/app/terminal/checkUpdateDeviceItems.ts +++ b/demo/angular/src/app/terminal/updateDeviceUpdateItems.ts @@ -1,7 +1,7 @@ import {ITestItems} from '../shared/interfaces'; import {TerminalEventsEnum} from '@capacitor-community/stripe-terminal'; -export const checkUpdateDeviceItems: ITestItems[] = [ +export const updateDeviceUpdateItems: ITestItems[] = [ { type: 'method', name: 'initialize', @@ -10,47 +10,46 @@ export const checkUpdateDeviceItems: ITestItems[] = [ type: 'event', name: TerminalEventsEnum.Loaded, }, - { type: 'method', - name: 'setSimulatorConfiguration:UPDATE_AVAILABLE', + name: 'discoverReaders', }, { type: 'event', - name: TerminalEventsEnum.ReportAvailableUpdate, + name: TerminalEventsEnum.DiscoveredReaders, }, { type: 'method', - name: 'setSimulatorConfiguration:REQUIRED', + name: 'connectReader', }, { type: 'event', - name: TerminalEventsEnum.StartInstallingUpdate, + name: TerminalEventsEnum.ConnectedReader, }, + { - type: 'event', - name: TerminalEventsEnum.ReaderSoftwareUpdateProgress, + type: 'method', + name: 'setSimulatorConfiguration:UPDATE_AVAILABLE', }, { type: 'event', - name: TerminalEventsEnum.FinishInstallingUpdate, + name: TerminalEventsEnum.ReportAvailableUpdate, }, - { type: 'method', - name: 'discoverReaders', + name: 'installAvailableUpdate', }, { type: 'event', - name: TerminalEventsEnum.DiscoveredReaders, + name: TerminalEventsEnum.StartInstallingUpdate, }, { - type: 'method', - name: 'connectReader', + type: 'event', + name: TerminalEventsEnum.ReaderSoftwareUpdateProgress, }, { - type: 'event', - name: TerminalEventsEnum.ConnectedReader, + type: 'method', + name: 'cancelInstallUpdate', }, { type: 'method', From 7d83465e32a89282e682e31197784d883d386c33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Mon, 12 Aug 2024 20:44:28 +0900 Subject: [PATCH 08/24] chore --- demo/angular/src/app/terminal/terminal.page.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/demo/angular/src/app/terminal/terminal.page.ts b/demo/angular/src/app/terminal/terminal.page.ts index 830ba09a..bff532ee 100644 --- a/demo/angular/src/app/terminal/terminal.page.ts +++ b/demo/angular/src/app/terminal/terminal.page.ts @@ -149,7 +149,6 @@ export class TerminalPage { ); if (readerType === TerminalConnectTypes.Internet) { - console.log('====setReaderDisplayは呼び出したよ') await StripeTerminal.setReaderDisplay({ currency: 'usd', tax: 0, @@ -251,7 +250,7 @@ export class TerminalPage { await StripeTerminal.installAvailableUpdate() .then(() => this.helper.updateItem(this.eventItems, 'installAvailableUpdate', true)); - await new Promise((resolve) => setTimeout(resolve, 1000)); + await new Promise((resolve) => setTimeout(resolve, 2000)); await StripeTerminal.cancelInstallUpdate() .then(() => this.helper.updateItem(this.eventItems, 'cancelInstallUpdate', true)); From 4e37307173111fe62a8c6a54f62fa2554e41b158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Mon, 12 Aug 2024 21:08:23 +0900 Subject: [PATCH 09/24] fmt --- packages/terminal/src/definitions.ts | 2 +- packages/terminal/src/web.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/terminal/src/definitions.ts b/packages/terminal/src/definitions.ts index cfa9c86f..1690cf61 100644 --- a/packages/terminal/src/definitions.ts +++ b/packages/terminal/src/definitions.ts @@ -36,7 +36,7 @@ export type CartLineItem = { export type Cart = { currency: string; tax: number; - total: number + total: number; lineItems: CartLineItem[]; }; diff --git a/packages/terminal/src/web.ts b/packages/terminal/src/web.ts index 0dd14c83..f561bacc 100644 --- a/packages/terminal/src/web.ts +++ b/packages/terminal/src/web.ts @@ -1,6 +1,6 @@ import { WebPlugin } from '@capacitor/core'; -import { +import type { StripeTerminalPlugin, TerminalConnectTypes, ReaderInterface, From 3f59da806a0d7efc2ff7e4b899b5b9631a8ddb3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Mon, 12 Aug 2024 22:43:03 +0900 Subject: [PATCH 10/24] feat(terminal): set reader interface --- .../stripe/terminal/StripeTerminal.java | 185 ++++++++++++++++-- .../terminal/ios/Plugin/StripeTerminal.swift | 63 ++++-- .../terminal/ios/Plugin/TerminalMappers.swift | 97 +++++++++ 3 files changed, 314 insertions(+), 31 deletions(-) diff --git a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java index 529b422e..4527afb6 100644 --- a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java +++ b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java @@ -36,8 +36,11 @@ import com.stripe.stripeterminal.external.models.ConnectionConfiguration.LocalMobileConnectionConfiguration; import com.stripe.stripeterminal.external.models.ConnectionConfiguration.UsbConnectionConfiguration; import com.stripe.stripeterminal.external.models.ConnectionStatus; +import com.stripe.stripeterminal.external.models.DeviceType; import com.stripe.stripeterminal.external.models.DisconnectReason; import com.stripe.stripeterminal.external.models.DiscoveryConfiguration; +import com.stripe.stripeterminal.external.models.Location; +import com.stripe.stripeterminal.external.models.LocationStatus; import com.stripe.stripeterminal.external.models.PaymentIntent; import com.stripe.stripeterminal.external.models.PaymentMethod; import com.stripe.stripeterminal.external.models.PaymentStatus; @@ -65,7 +68,7 @@ public class StripeTerminal extends Executor { private Cancelable discoveryCancelable; private Cancelable collectCancelable; private Cancelable installUpdateCancelable; - private List readers; + private List discoveredReadersList; private String locationId; private PluginCall collectCall; private PluginCall confirmPaymentIntentCall; @@ -82,7 +85,7 @@ public StripeTerminal( ) { super(contextSupplier, activitySupplier, notifyListenersFunction, pluginLogTag, "StripeTerminalExecutor"); this.contextSupplier = contextSupplier; - this.readers = new ArrayList<>(); + this.discoveredReadersList = new ArrayList<>(); } public void initialize(final PluginCall call) throws TerminalException { @@ -194,11 +197,11 @@ public void onDiscoverReaders(final PluginCall call) { final DiscoveryListener discoveryListener = readers -> { // 検索したReaderの一覧をListenerで渡す Log.d(logTag, String.valueOf(readers.get(0).getSerialNumber())); - this.readers = readers; + this.discoveredReadersList = readers; JSArray readersJSObject = new JSArray(); int i = 0; - for (Reader reader : this.readers) { + for (Reader reader : this.discoveredReadersList) { readersJSObject.put(convertReaderInterface(reader).put("index", String.valueOf(i))); } this.notifyListeners(TerminalEnumEvent.DiscoveredReaders.getWebEventName(), new JSObject().put("readers", readersJSObject)); @@ -273,8 +276,12 @@ public void onFailure(@NonNull TerminalException ex) { private void connectLocalMobileReader(final PluginCall call) { JSObject reader = call.getObject("reader"); + String serialNumber = reader.getString("serialNumber"); - if (reader.getInteger("index") == null) { + Reader foundReader = + this.discoveredReadersList.stream().filter(device -> serialNumber.equals(device.getSerialNumber())).findFirst().orElse(null); + + if (serialNumber == null || foundReader == null) { call.reject("The reader value is not set correctly."); return; } @@ -286,7 +293,7 @@ private void connectLocalMobileReader(final PluginCall call) { autoReconnectOnUnexpectedDisconnect, this.readerReconnectionListener ); - Terminal.getInstance().connectLocalMobileReader(this.readers.get(reader.getInteger("index")), config, this.readerCallback(call)); + Terminal.getInstance().connectLocalMobileReader(foundReader, config, this.readerCallback(call)); } ReaderReconnectionListener readerReconnectionListener = new ReaderReconnectionListener() { @@ -309,20 +316,47 @@ public void onReaderReconnectFailed(@NonNull Reader reader) { private void connectInternetReader(final PluginCall call) { JSObject reader = call.getObject("reader"); + String serialNumber = reader.getString("serialNumber"); + + Reader foundReader = + this.discoveredReadersList.stream().filter(device -> serialNumber.equals(device.getSerialNumber())).findFirst().orElse(null); + + if (serialNumber == null || foundReader == null) { + call.reject("The reader value is not set correctly."); + return; + } + InternetConnectionConfiguration config = new InternetConnectionConfiguration(); - Terminal.getInstance().connectInternetReader(this.readers.get(reader.getInteger("index")), config, this.readerCallback(call)); + Terminal.getInstance().connectInternetReader(foundReader, config, this.readerCallback(call)); } private void connectUsbReader(final PluginCall call) { JSObject reader = call.getObject("reader"); + String serialNumber = reader.getString("serialNumber"); + + Reader foundReader = + this.discoveredReadersList.stream().filter(device -> serialNumber.equals(device.getSerialNumber())).findFirst().orElse(null); + + if (serialNumber == null || foundReader == null) { + call.reject("The reader value is not set correctly."); + return; + } + UsbConnectionConfiguration config = new UsbConnectionConfiguration(this.locationId); - Terminal - .getInstance() - .connectUsbReader(this.readers.get(reader.getInteger("index")), config, this.readerListener(), this.readerCallback(call)); + Terminal.getInstance().connectUsbReader(foundReader, config, this.readerListener(), this.readerCallback(call)); } private void connectBluetoothReader(final PluginCall call) { JSObject reader = call.getObject("reader"); + String serialNumber = reader.getString("serialNumber"); + + Reader foundReader = + this.discoveredReadersList.stream().filter(device -> serialNumber.equals(device.getSerialNumber())).findFirst().orElse(null); + + if (serialNumber == null || foundReader == null) { + call.reject("The reader value is not set correctly."); + return; + } Boolean autoReconnectOnUnexpectedDisconnect = call.getBoolean("autoReconnectOnUnexpectedDisconnect", false); BluetoothConnectionConfiguration config = new BluetoothConnectionConfiguration( @@ -330,9 +364,7 @@ private void connectBluetoothReader(final PluginCall call) { autoReconnectOnUnexpectedDisconnect, this.readerReconnectionListener ); - Terminal - .getInstance() - .connectBluetoothReader(this.readers.get(reader.getInteger("index")), config, this.readerListener(), this.readerCallback(call)); + Terminal.getInstance().connectBluetoothReader(foundReader, config, this.readerListener(), this.readerCallback(call)); } public void cancelDiscoverReaders(final PluginCall call) { @@ -694,7 +726,132 @@ public void onDisconnect(@NotNull DisconnectReason reason) { } private JSObject convertReaderInterface(Reader reader) { - return new JSObject().put("serialNumber", reader.getSerialNumber()); + return new JSObject() + .put("label", reader.getLabel()) + .put("serialNumber", reader.getSerialNumber()) + .put("id", reader.getId()) + .put("locationId", reader.getLocation() != null ? reader.getLocation().getId() : null) + .put("deviceSoftwareVersion", reader.getDeviceSwVersion$external_publish()) + .put("simulated", reader.isSimulated()) + .put("serialNumber", reader.getSerialNumber()) + .put("ipAddress", reader.getIpAddress()) + .put("baseUrl", reader.getBaseUrl()) + .put("bootloaderVersion", reader.getBootloaderVersion()) + .put("configVersion", reader.getConfigVersion()) + .put("emvKeyProfileId", reader.getEmvKeyProfileId()) + .put("firmwareVersion", reader.getFirmwareVersion()) + .put("hardwareVersion", reader.getHardwareVersion()) + .put("macKeyProfileId", reader.getMacKeyProfileId()) + .put("pinKeyProfileId", reader.getPinKeyProfileId()) + .put("trackKeyProfileId", reader.getTrackKeyProfileId()) + .put("settingsVersion", reader.getSettingsVersion()) + .put("pinKeysetId", reader.getPinKeysetId()) + .put("deviceType", mapFromDeviceType(reader.getDeviceType())) + .put("status", mapFromNetworkStatus(reader.getNetworkStatus())) + .put("locationStatus", mapFromLocationStatus(reader.getLocationStatus())) + .put("batteryLevel", reader.getBatteryLevel() != null ? reader.getBatteryLevel().doubleValue() : null) + .put("availableUpdate", mapFromReaderSoftwareUpdate(reader.getAvailableUpdate())) + .put("location", mapFromLocation(reader.getLocation())); + } + + public static JSObject mapFromLocation(Location location) { + if (location == null) { + return new JSObject(); + } + + JSObject address = new JSObject(); + + if (location.getAddress() != null) { + address + .put("country", location.getAddress().getCountry()) + .put("city", location.getAddress().getCity()) + .put("postalCode", location.getAddress().getPostalCode()) + .put("line1", location.getAddress().getLine1()) + .put("line2", location.getAddress().getLine2()) + .put("state", location.getAddress().getState()); + } + + return new JSObject() + .put("id", location.getId()) + .put("displayName", location.getDisplayName()) + .put("address", address) + .put("livemode", location.getLivemode()); + } + + public static JSObject mapFromReaderSoftwareUpdate(ReaderSoftwareUpdate update) { + if (update == null) { + return new JSObject(); + } + + return new JSObject() + .put("deviceSoftwareVersion", update.getVersion()) + .put("estimatedUpdateTime", update.getTimeEstimate().toString()) + .put("requiredAt", update.getRequiredAt().getTime()); + } + + public static String mapFromLocationStatus(LocationStatus status) { + if (status == null) { + return "unknown"; + } + + return switch (status) { + case NOT_SET -> "notSet"; + case SET -> "set"; + case UNKNOWN -> "unknown"; + default -> "unknown"; + }; + } + + public static String mapFromNetworkStatus(Reader.NetworkStatus status) { + if (status == null) { + return "unknown"; + } + + return switch (status) { + case OFFLINE -> "offline"; + case ONLINE -> "online"; + default -> "unknown"; + }; + } + + private String mapFromDeviceType(DeviceType type) { + switch (type) { + case CHIPPER_1X: + return "chipper1X"; + case CHIPPER_2X: + return "chipper2X"; + case COTS_DEVICE: + return "cotsDevice"; + case ETNA: + return "etna"; + case STRIPE_M2: + return "stripeM2"; + case STRIPE_S700: + return "stripeS700"; + case STRIPE_S700_DEVKIT: + return "stripeS700Devkit"; + // React Native has this model. deprecated? + // case STRIPE_S710: + // return "stripeS710"; + // case STRIPE_S710_DEVKIT: + // return "stripeS710Devkit"; + case UNKNOWN: + return "unknown"; + case VERIFONE_P400: + return "verifoneP400"; + case WISECUBE: + return "wiseCube"; + case WISEPAD_3: + return "wisePad3"; + case WISEPAD_3S: + return "wisePad3s"; + case WISEPOS_E: + return "wisePosE"; + case WISEPOS_E_DEVKIT: + return "wisePosEDevkit"; + default: + throw new IllegalArgumentException("Unknown DeviceType: " + type); + } } private JSObject convertReaderSoftwareUpdate(ReaderSoftwareUpdate update) { diff --git a/packages/terminal/ios/Plugin/StripeTerminal.swift b/packages/terminal/ios/Plugin/StripeTerminal.swift index 0be64e51..13e3fde0 100644 --- a/packages/terminal/ios/Plugin/StripeTerminal.swift +++ b/packages/terminal/ios/Plugin/StripeTerminal.swift @@ -18,7 +18,7 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg var isInitialize: Bool = false var paymentIntent: PaymentIntent? - var readers: [Reader]? + var discoveredReadersList: [Reader]? @objc public func initialize(_ call: CAPPluginCall) { self.isTest = call.getBool("isTest", true) @@ -90,12 +90,11 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg var i = 0 for reader in readers { readersJSObject.append([ - "index": i, - "serialNumber": reader.serialNumber - ]) + "index": i + ].merging(self.convertReaderInterface(reader: reader)) { (_, new) in new }) i += 1 } - self.readers = readers + self.discoveredReadersList = readers self.plugin?.notifyListeners(TerminalEvents.DiscoveredReaders.rawValue, data: ["readers": readersJSObject]) self.discoverCall?.resolve([ @@ -116,9 +115,7 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg public func getConnectedReader(_ call: CAPPluginCall) { if let reader = Terminal.shared.connectedReader { - call.resolve(["reader": [ - "serialNumber": reader.serialNumber - ]]) + call.resolve(["reader": self.convertReaderInterface(reader: reader)]) } else { call.resolve(["reader": nil]) } @@ -143,16 +140,21 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg } private func connectLocalMobileReader(_ call: CAPPluginCall) { - let reader: JSObject = call.getObject("reader")! let autoReconnectOnUnexpectedDisconnect = call.getBool("autoReconnectOnUnexpectedDisconnect", false) - let index: Int = reader["index"] as! Int + let reader: JSObject = call.getObject("reader")! + let serialNumber: String = reader["serialNumber"] as! String let connectionConfig = try! LocalMobileConnectionConfigurationBuilder.init(locationId: self.locationId!) .setAutoReconnectOnUnexpectedDisconnect(autoReconnectOnUnexpectedDisconnect) .setAutoReconnectionDelegate(autoReconnectOnUnexpectedDisconnect ? self : nil) .build() - Terminal.shared.connectLocalMobileReader(self.readers![index], delegate: self, connectionConfig: connectionConfig) { reader, error in + guard let foundReader = self.discoveredReadersList?.first(where: { $0.serialNumber == serialNumber }) else { + call.reject("reader is not match from descovered readers.") + return + } + + Terminal.shared.connectLocalMobileReader(foundReader, delegate: self, connectionConfig: connectionConfig) { reader, error in if let reader = reader { self.plugin?.notifyListeners(TerminalEvents.ConnectedReader.rawValue, data: [:]) call.resolve() @@ -164,13 +166,18 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg private func connectInternetReader(_ call: CAPPluginCall) { let reader: JSObject = call.getObject("reader")! - let index: Int = reader["index"] as! Int + let serialNumber: String = reader["serialNumber"] as! String + + guard let foundReader = self.discoveredReadersList?.first(where: { $0.serialNumber == serialNumber }) else { + call.reject("reader is not match from descovered readers.") + return + } let config = try! InternetConnectionConfigurationBuilder() .setFailIfInUse(true) .build() - Terminal.shared.connectInternetReader(self.readers![index], connectionConfig: config) { reader, error in + Terminal.shared.connectInternetReader(foundReader, connectionConfig: config) { reader, error in if let reader = reader { self.plugin?.notifyListeners(TerminalEvents.ConnectedReader.rawValue, data: [:]) call.resolve() @@ -182,14 +189,20 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg private func connectBluetoothReader(_ call: CAPPluginCall) { let reader: JSObject = call.getObject("reader")! - let index: Int = reader["index"] as! Int + let serialNumber: String = reader["serialNumber"] as! String + + guard let foundReader = self.discoveredReadersList?.first(where: { $0.serialNumber == serialNumber }) else { + call.reject("reader is not match from descovered readers.") + return + } + let autoReconnectOnUnexpectedDisconnect = call.getBool("autoReconnectOnUnexpectedDisconnect", false) let config = try! BluetoothConnectionConfigurationBuilder(locationId: self.locationId!) .setAutoReconnectOnUnexpectedDisconnect(autoReconnectOnUnexpectedDisconnect) .setAutoReconnectionDelegate(autoReconnectOnUnexpectedDisconnect ? self : nil) .build() - Terminal.shared.connectBluetoothReader(self.readers![index], delegate: self, connectionConfig: config) { reader, error in + Terminal.shared.connectBluetoothReader(foundReader, delegate: self, connectionConfig: config) { reader, error in if let reader = reader { self.plugin?.notifyListeners(TerminalEvents.ConnectedReader.rawValue, data: [:]) call.resolve() @@ -468,8 +481,24 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg /* * Private */ - private func convertReaderInterface(reader: Reader) -> [String: String] { - return ["serialNumber": reader.serialNumber] + private func convertReaderInterface(reader: Reader) -> JSObject { + return [ + "label": reader.label ?? NSNull(), + "batteryLevel": (reader.batteryLevel ?? 0).intValue, + "batteryStatus": TerminalMappers.mapFromBatteryStatus(reader.batteryStatus), + "simulated": reader.simulated, + "serialNumber": reader.serialNumber, + "isCharging": (reader.isCharging ?? 0).intValue, + "id": reader.stripeId ?? NSNull(), + "availableUpdate": TerminalMappers.mapFromReaderSoftwareUpdate(reader.availableUpdate), + "locationId": reader.locationId ?? NSNull(), + "ipAddress": reader.ipAddress ?? NSNull(), + "status": TerminalMappers.mapFromReaderNetworkStatus(reader.status), + "location": TerminalMappers.mapFromLocation(reader.location), + "locationStatus": TerminalMappers.mapFromLocationStatus(reader.locationStatus), + "deviceType": TerminalMappers.mapFromDeviceType(reader.deviceType), + "deviceSoftwareVersion": reader.deviceSoftwareVersion ?? NSNull() + ] } private func convertReaderSoftwareUpdate(update: ReaderSoftwareUpdate) -> [String: String] { diff --git a/packages/terminal/ios/Plugin/TerminalMappers.swift b/packages/terminal/ios/Plugin/TerminalMappers.swift index 0ab43deb..e21df5a4 100644 --- a/packages/terminal/ios/Plugin/TerminalMappers.swift +++ b/packages/terminal/ios/Plugin/TerminalMappers.swift @@ -2,6 +2,103 @@ import StripeTerminal import Capacitor class TerminalMappers { + class func mapFromDeviceType(_ type: DeviceType) -> String { + switch type { + case DeviceType.appleBuiltIn: return "appleBuiltIn" + case DeviceType.chipper1X: return "chipper1X" + case DeviceType.chipper2X: return "chipper2X" + case DeviceType.etna: return "etna" + case DeviceType.stripeM2: return "stripeM2" + case DeviceType.stripeS700: return "stripeS700" + case DeviceType.stripeS700DevKit: return "stripeS700Devkit" + case DeviceType.verifoneP400: return "verifoneP400" + case DeviceType.wiseCube: return "wiseCube" + case DeviceType.wisePad3: return "wisePad3" + case DeviceType.wisePosE: return "wisePosE" + case DeviceType.wisePosEDevKit: return "wisePosEDevkit" + default: return "unknown" + } + } + + class func mapFromAddress(_ address: Address?) -> JSObject { + if let address = address { + let result: JSObject = [ + "city": address.city ?? NSNull(), + "country": address.country ?? NSNull(), + "postalCode": address.postalCode ?? NSNull(), + "line1": address.line1 ?? NSNull(), + "line2": address.line2 ?? NSNull(), + "state": address.state ?? NSNull() + ] + return result + } else { + return JSObject() + } + } + + class func mapFromLocation(_ location: Location?) -> JSObject { + guard let unwrappedLocation = location else { + return [:] + } + let result: JSObject = [ + "displayName": unwrappedLocation.displayName ?? NSNull(), + "id": unwrappedLocation.stripeId, + "livemode": unwrappedLocation.livemode, + "address": mapFromAddress(unwrappedLocation.address) + ] + return result + } + + class func mapFromLocationStatus(_ status: LocationStatus) -> String { + switch status { + case LocationStatus.notSet: return "notSet" + case LocationStatus.set: return "set" + case LocationStatus.unknown: return "unknown" + default: return "unknown" + } + } + + class func mapFromLocationsList(_ locations: [Location]) -> JSArray { + var list: JSArray = [] + + for location in locations { + let result = mapFromLocation(location) + if result.count != 0 { + list.append(result) + } + } + + return list + } + + class func mapFromReaderNetworkStatus(_ status: ReaderNetworkStatus) -> String { + switch status { + case ReaderNetworkStatus.offline: return "offline" + case ReaderNetworkStatus.online: return "online" + default: return "unknown" + } + } + + class func convertDateToUnixTimestamp(date: Date?) -> String { + if let date = date { + let value = date.timeIntervalSince1970 * 1000.0 + return String(format: "%.0f", value) + } + return "" + } + + class func mapFromReaderSoftwareUpdate(_ update: ReaderSoftwareUpdate?) -> JSObject { + guard let unwrappedUpdate = update else { + return JSObject() + } + let result: JSObject = [ + "deviceSoftwareVersion": unwrappedUpdate.deviceSoftwareVersion, + "estimatedUpdateTime": mapFromUpdateTimeEstimate(unwrappedUpdate.estimatedUpdateTime), + "requiredAt": convertDateToUnixTimestamp(date: unwrappedUpdate.requiredAt) + ] + return result + } + class func mapToCartLineItem(_ cartLineItem: NSDictionary) -> CartLineItem? { guard let displayName = cartLineItem["displayName"] as? String else { return nil } guard let quantity = cartLineItem["quantity"] as? NSNumber else { return nil } From 09d00bdd78f90501da247e40ae46915196d14984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Mon, 12 Aug 2024 22:51:39 +0900 Subject: [PATCH 11/24] feat(terminal): follow ReaderSoftwareUpdateInterface to reader --- .../stripe/terminal/StripeTerminal.java | 6 +----- .../terminal/ios/Plugin/StripeTerminal.swift | 21 ++++++++++--------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java index 4527afb6..85ac2662 100644 --- a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java +++ b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java @@ -855,10 +855,6 @@ private String mapFromDeviceType(DeviceType type) { } private JSObject convertReaderSoftwareUpdate(ReaderSoftwareUpdate update) { - return new JSObject() - .put("version", update.getVersion()) - .put("settingsVersion", update.getSettingsVersion()) - .put("requiredAt", update.getRequiredAt().getTime()) - .put("timeEstimate", update.getTimeEstimate().toString()); + return mapFromReaderSoftwareUpdate(update); } } diff --git a/packages/terminal/ios/Plugin/StripeTerminal.swift b/packages/terminal/ios/Plugin/StripeTerminal.swift index 13e3fde0..369c4d46 100644 --- a/packages/terminal/ios/Plugin/StripeTerminal.swift +++ b/packages/terminal/ios/Plugin/StripeTerminal.swift @@ -373,7 +373,9 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg public func localMobileReader(_ reader: Reader, didStartInstallingUpdate update: ReaderSoftwareUpdate, cancelable: Cancelable?) { self.installUpdateCancelable = cancelable - self.plugin?.notifyListeners(TerminalEvents.StartInstallingUpdate.rawValue, data: self.convertReaderSoftwareUpdate(update: update)) + self.plugin?.notifyListeners(TerminalEvents.StartInstallingUpdate.rawValue, data: [ + "update": self.convertReaderSoftwareUpdate(update: update) + ]) } public func localMobileReader(_ reader: Reader, didReportReaderSoftwareUpdateProgress progress: Float) { @@ -410,12 +412,16 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg */ public func reader(_: Reader, didReportAvailableUpdate update: ReaderSoftwareUpdate) { - self.plugin?.notifyListeners(TerminalEvents.ReportAvailableUpdate.rawValue, data: self.convertReaderSoftwareUpdate(update: update)) + self.plugin?.notifyListeners(TerminalEvents.ReportAvailableUpdate.rawValue, data: [ + "update": self.convertReaderSoftwareUpdate(update: update) + ]) } public func reader(_: Reader, didStartInstallingUpdate update: ReaderSoftwareUpdate, cancelable: Cancelable?) { self.installUpdateCancelable = cancelable - self.plugin?.notifyListeners(TerminalEvents.StartInstallingUpdate.rawValue, data: self.convertReaderSoftwareUpdate(update: update)) + self.plugin?.notifyListeners(TerminalEvents.StartInstallingUpdate.rawValue, data: [ + "update": self.convertReaderSoftwareUpdate(update: update) + ]) } public func reader(_: Reader, didReportReaderSoftwareUpdateProgress progress: Float) { @@ -501,13 +507,8 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg ] } - private func convertReaderSoftwareUpdate(update: ReaderSoftwareUpdate) -> [String: String] { - return [ - "version": update.deviceSoftwareVersion, - "settingsVersion": update.deviceSoftwareVersion, - "requiredAt": update.requiredAt.description, - "timeEstimate": TerminalMappers.mapFromUpdateTimeEstimate(update.estimatedUpdateTime) - ] + private func convertReaderSoftwareUpdate(update: ReaderSoftwareUpdate) -> JSObject { + return TerminalMappers.mapFromReaderSoftwareUpdate(update) } } From d6b6028edf48fc6283ca584205075c8229205f5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Mon, 12 Aug 2024 22:51:47 +0900 Subject: [PATCH 12/24] chore --- packages/terminal/src/definitions.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/terminal/src/definitions.ts b/packages/terminal/src/definitions.ts index 1690cf61..dc4476ef 100644 --- a/packages/terminal/src/definitions.ts +++ b/packages/terminal/src/definitions.ts @@ -21,10 +21,9 @@ export type ReaderInterface = { }; export type ReaderSoftwareUpdateInterface = { - version: string; - settingsVersion: string; + deviceSoftwareVersion: string; + estimatedUpdateTime: UpdateTimeEstimate; requiredAt: number; - timeEstimate: UpdateTimeEstimate; }; export type CartLineItem = { From e7674ea21120214f2ca3777096d08646da4477ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Mon, 12 Aug 2024 22:55:35 +0900 Subject: [PATCH 13/24] refactor(terminal): separate terminalMapper at android --- .../stripe/terminal/StripeTerminal.java | 115 ++---------------- .../terminal/helper/TerminalMappers.java | 111 +++++++++++++++++ 2 files changed, 120 insertions(+), 106 deletions(-) create mode 100644 packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/helper/TerminalMappers.java diff --git a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java index 85ac2662..de55003a 100644 --- a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java +++ b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java @@ -14,6 +14,7 @@ import com.getcapacitor.JSArray; import com.getcapacitor.JSObject; import com.getcapacitor.PluginCall; +import com.getcapacitor.community.stripe.terminal.helper.TerminalMappers; import com.getcapacitor.community.stripe.terminal.models.Executor; import com.google.android.gms.common.util.BiConsumer; import com.stripe.stripeterminal.Terminal; @@ -77,6 +78,8 @@ public class StripeTerminal extends Executor { private TerminalConnectTypes terminalConnectType; private PaymentIntent paymentIntentInstance; + private final TerminalMappers terminalMappers = new TerminalMappers(); + public StripeTerminal( Supplier contextSupplier, Supplier activitySupplier, @@ -746,115 +749,15 @@ private JSObject convertReaderInterface(Reader reader) { .put("trackKeyProfileId", reader.getTrackKeyProfileId()) .put("settingsVersion", reader.getSettingsVersion()) .put("pinKeysetId", reader.getPinKeysetId()) - .put("deviceType", mapFromDeviceType(reader.getDeviceType())) - .put("status", mapFromNetworkStatus(reader.getNetworkStatus())) - .put("locationStatus", mapFromLocationStatus(reader.getLocationStatus())) + .put("deviceType", terminalMappers.mapFromDeviceType(reader.getDeviceType())) + .put("status", terminalMappers.mapFromNetworkStatus(reader.getNetworkStatus())) + .put("locationStatus", terminalMappers.mapFromLocationStatus(reader.getLocationStatus())) .put("batteryLevel", reader.getBatteryLevel() != null ? reader.getBatteryLevel().doubleValue() : null) - .put("availableUpdate", mapFromReaderSoftwareUpdate(reader.getAvailableUpdate())) - .put("location", mapFromLocation(reader.getLocation())); - } - - public static JSObject mapFromLocation(Location location) { - if (location == null) { - return new JSObject(); - } - - JSObject address = new JSObject(); - - if (location.getAddress() != null) { - address - .put("country", location.getAddress().getCountry()) - .put("city", location.getAddress().getCity()) - .put("postalCode", location.getAddress().getPostalCode()) - .put("line1", location.getAddress().getLine1()) - .put("line2", location.getAddress().getLine2()) - .put("state", location.getAddress().getState()); - } - - return new JSObject() - .put("id", location.getId()) - .put("displayName", location.getDisplayName()) - .put("address", address) - .put("livemode", location.getLivemode()); - } - - public static JSObject mapFromReaderSoftwareUpdate(ReaderSoftwareUpdate update) { - if (update == null) { - return new JSObject(); - } - - return new JSObject() - .put("deviceSoftwareVersion", update.getVersion()) - .put("estimatedUpdateTime", update.getTimeEstimate().toString()) - .put("requiredAt", update.getRequiredAt().getTime()); - } - - public static String mapFromLocationStatus(LocationStatus status) { - if (status == null) { - return "unknown"; - } - - return switch (status) { - case NOT_SET -> "notSet"; - case SET -> "set"; - case UNKNOWN -> "unknown"; - default -> "unknown"; - }; - } - - public static String mapFromNetworkStatus(Reader.NetworkStatus status) { - if (status == null) { - return "unknown"; - } - - return switch (status) { - case OFFLINE -> "offline"; - case ONLINE -> "online"; - default -> "unknown"; - }; - } - - private String mapFromDeviceType(DeviceType type) { - switch (type) { - case CHIPPER_1X: - return "chipper1X"; - case CHIPPER_2X: - return "chipper2X"; - case COTS_DEVICE: - return "cotsDevice"; - case ETNA: - return "etna"; - case STRIPE_M2: - return "stripeM2"; - case STRIPE_S700: - return "stripeS700"; - case STRIPE_S700_DEVKIT: - return "stripeS700Devkit"; - // React Native has this model. deprecated? - // case STRIPE_S710: - // return "stripeS710"; - // case STRIPE_S710_DEVKIT: - // return "stripeS710Devkit"; - case UNKNOWN: - return "unknown"; - case VERIFONE_P400: - return "verifoneP400"; - case WISECUBE: - return "wiseCube"; - case WISEPAD_3: - return "wisePad3"; - case WISEPAD_3S: - return "wisePad3s"; - case WISEPOS_E: - return "wisePosE"; - case WISEPOS_E_DEVKIT: - return "wisePosEDevkit"; - default: - throw new IllegalArgumentException("Unknown DeviceType: " + type); - } + .put("availableUpdate", terminalMappers.mapFromReaderSoftwareUpdate(reader.getAvailableUpdate())) + .put("location", terminalMappers.mapFromLocation(reader.getLocation())); } private JSObject convertReaderSoftwareUpdate(ReaderSoftwareUpdate update) { - return mapFromReaderSoftwareUpdate(update); + return terminalMappers.mapFromReaderSoftwareUpdate(update); } } diff --git a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/helper/TerminalMappers.java b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/helper/TerminalMappers.java new file mode 100644 index 00000000..be6337ed --- /dev/null +++ b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/helper/TerminalMappers.java @@ -0,0 +1,111 @@ +package com.getcapacitor.community.stripe.terminal.helper; + +import com.getcapacitor.JSObject; +import com.stripe.stripeterminal.external.models.DeviceType; +import com.stripe.stripeterminal.external.models.Location; +import com.stripe.stripeterminal.external.models.LocationStatus; +import com.stripe.stripeterminal.external.models.Reader; +import com.stripe.stripeterminal.external.models.ReaderSoftwareUpdate; + +public class TerminalMappers { + + public JSObject mapFromLocation(Location location) { + if (location == null) { + return new JSObject(); + } + + JSObject address = new JSObject(); + + if (location.getAddress() != null) { + address + .put("country", location.getAddress().getCountry()) + .put("city", location.getAddress().getCity()) + .put("postalCode", location.getAddress().getPostalCode()) + .put("line1", location.getAddress().getLine1()) + .put("line2", location.getAddress().getLine2()) + .put("state", location.getAddress().getState()); + } + + return new JSObject() + .put("id", location.getId()) + .put("displayName", location.getDisplayName()) + .put("address", address) + .put("livemode", location.getLivemode()); + } + + public JSObject mapFromReaderSoftwareUpdate(ReaderSoftwareUpdate update) { + if (update == null) { + return new JSObject(); + } + + return new JSObject() + .put("deviceSoftwareVersion", update.getVersion()) + .put("estimatedUpdateTime", update.getTimeEstimate().toString()) + .put("requiredAt", update.getRequiredAt().getTime()); + } + + public String mapFromLocationStatus(LocationStatus status) { + if (status == null) { + return "unknown"; + } + + return switch (status) { + case NOT_SET -> "notSet"; + case SET -> "set"; + case UNKNOWN -> "unknown"; + default -> "unknown"; + }; + } + + public String mapFromNetworkStatus(Reader.NetworkStatus status) { + if (status == null) { + return "unknown"; + } + + return switch (status) { + case OFFLINE -> "offline"; + case ONLINE -> "online"; + default -> "unknown"; + }; + } + + public String mapFromDeviceType(DeviceType type) { + switch (type) { + case CHIPPER_1X: + return "chipper1X"; + case CHIPPER_2X: + return "chipper2X"; + case COTS_DEVICE: + return "cotsDevice"; + case ETNA: + return "etna"; + case STRIPE_M2: + return "stripeM2"; + case STRIPE_S700: + return "stripeS700"; + case STRIPE_S700_DEVKIT: + return "stripeS700Devkit"; + // React Native has this model. deprecated? + // case STRIPE_S710: + // return "stripeS710"; + // case STRIPE_S710_DEVKIT: + // return "stripeS710Devkit"; + case UNKNOWN: + return "unknown"; + case VERIFONE_P400: + return "verifoneP400"; + case WISECUBE: + return "wiseCube"; + case WISEPAD_3: + return "wisePad3"; + case WISEPAD_3S: + return "wisePad3s"; + case WISEPOS_E: + return "wisePosE"; + case WISEPOS_E_DEVKIT: + return "wisePosEDevkit"; + default: + throw new IllegalArgumentException("Unknown DeviceType: " + type); + } + } +} From 331e99c04b32f80ef7aff60b5677160c9bc2e50a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Mon, 12 Aug 2024 23:09:31 +0900 Subject: [PATCH 14/24] feat(terminal): set ReaderInterface --- packages/terminal/README.md | 73 ++++++++++++------ .../terminal/helper/TerminalMappers.java | 16 ++-- .../terminal/ios/Plugin/TerminalMappers.swift | 14 ++-- packages/terminal/src/definitions.ts | 76 ++++++++++++++++++- packages/terminal/src/stripe.enum.ts | 12 +++ 5 files changed, 148 insertions(+), 43 deletions(-) diff --git a/packages/terminal/README.md b/packages/terminal/README.md index d6f2a857..f8315649 100644 --- a/packages/terminal/README.md +++ b/packages/terminal/README.md @@ -803,12 +803,22 @@ addListener(eventName: TerminalEventsEnum.ReaderReconnectFailed, listenerFunc: ( #### ReaderInterface -{ index: number; serialNumber: string; } +{ /** * The unique serial number is primary identifier inner plugin. */ serialNumber: string; label: string; batteryLevel: number; batteryStatus: BatteryStatus; simulated: boolean; id: number; availableUpdate: ReaderSoftwareUpdateInterface; locationId: string; ipAddress: string; status: NetworkStatus; location: LocationInterface; locationStatus: LocationStatus; deviceType: string; // TODO enum deviceSoftwareVersion: string; // iOS only isCharging: number; // Android only baseUrl: string; // Android only bootloaderVersion: string; // Android only configVersion: string; // Android only emvKeyProfileId: string; // Android only firmwareVersion: string; // Android only hardwareVersion: string; // Android only macKeyProfileId: string; // Android only pinKeyProfileId: string; // Android only trackKeyProfileId: string; // Android only settingsVersion: string; // Android only pinKeysetId: string; /** * @deprecated This property has been deprecated and should use the `serialNumber` property. */ index?: number; } + + +#### ReaderSoftwareUpdateInterface + +{ deviceSoftwareVersion: string; estimatedUpdateTime: UpdateTimeEstimate; requiredAt: number; } + + +#### LocationInterface + +{ id: string; displayName: string; address: { city: string; country: string; postalCode: string; line1: string; line2: string; state: string; }; ipAddress: string; } #### Cart -{ currency: string; tax: number; total: number lineItems: CartLineItem[]; } +{ currency: string; tax: number; total: number; lineItems: CartLineItem[]; } #### CartLineItem @@ -816,12 +826,45 @@ addListener(eventName: TerminalEventsEnum.ReaderReconnectFailed, listenerFunc: ( { displayName: string; quantity: number; amount: number; } -#### ReaderSoftwareUpdateInterface +### Enums -{ version: string; settingsVersion: string; requiredAt: number; timeEstimate: UpdateTimeEstimate; } +#### BatteryStatus -### Enums +| Members | Value | +| -------------- | ----------------------- | +| **`Unknown`** | 'UNKNOWN' | +| **`Critical`** | 'CRITICAL' | +| **`Low`** | 'LOW' | +| **`Nominal`** | 'NOMINAL' | + + +#### UpdateTimeEstimate + +| Members | Value | +| -------------------------- | -------------------------------------- | +| **`LessThanOneMinute`** | 'LESS_THAN_ONE_MINUTE' | +| **`OneToTwoMinutes`** | 'ONE_TO_TWO_MINUTES' | +| **`TwoToFiveMinutes`** | 'TWO_TO_FIVE_MINUTES' | +| **`FiveToFifteenMinutes`** | 'FIVE_TO_FIFTEEN_MINUTES' | + + +#### NetworkStatus + +| Members | Value | +| ------------- | ---------------------- | +| **`Unknown`** | 'UNKNOWN' | +| **`Online`** | 'ONLINE' | +| **`Offline`** | 'OFFLINE' | + + +#### LocationStatus + +| Members | Value | +| ------------- | ---------------------- | +| **`NotSet`** | 'NOT_SET' | +| **`Set`** | 'SET' | +| **`Unknown`** | 'UNKNOWN' | #### TerminalConnectTypes @@ -933,26 +976,6 @@ addListener(eventName: TerminalEventsEnum.ReaderReconnectFailed, listenerFunc: ( | **`Connected`** | 'CONNECTED' | -#### UpdateTimeEstimate - -| Members | Value | -| -------------------------- | -------------------------------------- | -| **`LessThanOneMinute`** | 'LESS_THAN_ONE_MINUTE' | -| **`OneToTwoMinutes`** | 'ONE_TO_TWO_MINUTES' | -| **`TwoToFiveMinutes`** | 'TWO_TO_FIVE_MINUTES' | -| **`FiveToFifteenMinutes`** | 'FIVE_TO_FIFTEEN_MINUTES' | - - -#### BatteryStatus - -| Members | Value | -| -------------- | ----------------------- | -| **`Unknown`** | 'UNKNOWN' | -| **`Critical`** | 'CRITICAL' | -| **`Low`** | 'LOW' | -| **`Nominal`** | 'NOMINAL' | - - #### ReaderEvent | Members | Value | diff --git a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/helper/TerminalMappers.java b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/helper/TerminalMappers.java index be6337ed..0babcea6 100644 --- a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/helper/TerminalMappers.java +++ b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/helper/TerminalMappers.java @@ -46,14 +46,14 @@ public JSObject mapFromReaderSoftwareUpdate(ReaderSoftwareUpdate update) { public String mapFromLocationStatus(LocationStatus status) { if (status == null) { - return "unknown"; + return "UNKNOWN"; } return switch (status) { - case NOT_SET -> "notSet"; - case SET -> "set"; - case UNKNOWN -> "unknown"; - default -> "unknown"; + case NOT_SET -> "NOT_SET"; + case SET -> "SET"; + case UNKNOWN -> "UNKNOWN"; + default -> "UNKNOWN"; }; } @@ -63,9 +63,9 @@ public String mapFromNetworkStatus(Reader.NetworkStatus status) { } return switch (status) { - case OFFLINE -> "offline"; - case ONLINE -> "online"; - default -> "unknown"; + case OFFLINE -> "OFFLINE"; + case ONLINE -> "ONLINE"; + default -> "UNKNOWN"; }; } diff --git a/packages/terminal/ios/Plugin/TerminalMappers.swift b/packages/terminal/ios/Plugin/TerminalMappers.swift index e21df5a4..af942fd3 100644 --- a/packages/terminal/ios/Plugin/TerminalMappers.swift +++ b/packages/terminal/ios/Plugin/TerminalMappers.swift @@ -51,10 +51,10 @@ class TerminalMappers { class func mapFromLocationStatus(_ status: LocationStatus) -> String { switch status { - case LocationStatus.notSet: return "notSet" - case LocationStatus.set: return "set" - case LocationStatus.unknown: return "unknown" - default: return "unknown" + case LocationStatus.notSet: return "NOT_SET" + case LocationStatus.set: return "SET" + case LocationStatus.unknown: return "UNKNOWN" + default: return "UNKNOWN" } } @@ -73,9 +73,9 @@ class TerminalMappers { class func mapFromReaderNetworkStatus(_ status: ReaderNetworkStatus) -> String { switch status { - case ReaderNetworkStatus.offline: return "offline" - case ReaderNetworkStatus.online: return "online" - default: return "unknown" + case ReaderNetworkStatus.offline: return "OFFLINE" + case ReaderNetworkStatus.online: return "ONLINE" + default: return "UNKNOWN" } } diff --git a/packages/terminal/src/definitions.ts b/packages/terminal/src/definitions.ts index dc4476ef..aea894ca 100644 --- a/packages/terminal/src/definitions.ts +++ b/packages/terminal/src/definitions.ts @@ -1,7 +1,7 @@ import type { PluginListenerHandle } from '@capacitor/core'; import type { TerminalEventsEnum } from './events.enum'; -import type { +import { TerminalConnectTypes, UpdateTimeEstimate, SimulateReaderUpdate, @@ -12,12 +12,82 @@ import type { ReaderInputOption, PaymentStatus, DisconnectReason, - ConnectionStatus, + ConnectionStatus, NetworkStatus, LocationStatus, } from './stripe.enum'; export type ReaderInterface = { - index: number; + /** + * The unique serial number is primary identifier inner plugin. + */ serialNumber: string; + + label: string; + batteryLevel: number; + batteryStatus: BatteryStatus; + simulated: boolean; + id: number; + availableUpdate: ReaderSoftwareUpdateInterface; + locationId: string; + ipAddress: string; + status: NetworkStatus; + location: LocationInterface; + locationStatus: LocationStatus; + deviceType: string; // TODO enum + deviceSoftwareVersion: string; + + // iOS only + isCharging: number; + + // Android only + baseUrl: string; + + // Android only + bootloaderVersion: string; + + // Android only + configVersion: string; + + // Android only + emvKeyProfileId: string; + + // Android only + firmwareVersion: string; + + // Android only + hardwareVersion: string; + + // Android only + macKeyProfileId: string; + + // Android only + pinKeyProfileId: string; + + // Android only + trackKeyProfileId: string; + + // Android only + settingsVersion: string; + + // Android only + pinKeysetId: string; + + /** + * @deprecated This property has been deprecated and should use the `serialNumber` property. + */ + index?: number; +}; +export type LocationInterface = { + id: string; + displayName: string; + address: { + city: string; + country: string; + postalCode: string; + line1: string; + line2: string; + state: string; + }; + ipAddress: string; }; export type ReaderSoftwareUpdateInterface = { diff --git a/packages/terminal/src/stripe.enum.ts b/packages/terminal/src/stripe.enum.ts index b4e0f246..3cee3410 100644 --- a/packages/terminal/src/stripe.enum.ts +++ b/packages/terminal/src/stripe.enum.ts @@ -60,6 +60,18 @@ export enum BatteryStatus { Nominal = 'NOMINAL', } +export enum LocationStatus { + NotSet = 'NOT_SET', + Set = 'SET', + Unknown = 'UNKNOWN', +} + +export enum NetworkStatus { + Unknown = 'UNKNOWN', + Online = 'ONLINE', + Offline = 'OFFLINE', +} + export enum ReaderEvent { Unknown = 'UNKNOWN', CardInserted = 'CARD_INSERTED', From 1b294e01daacb0853c5e2f83e89413a12263722f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Mon, 12 Aug 2024 23:12:15 +0900 Subject: [PATCH 15/24] chore --- packages/terminal/src/definitions.ts | 29 +++++++--------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/packages/terminal/src/definitions.ts b/packages/terminal/src/definitions.ts index aea894ca..bfc56dee 100644 --- a/packages/terminal/src/definitions.ts +++ b/packages/terminal/src/definitions.ts @@ -35,40 +35,25 @@ export type ReaderInterface = { deviceType: string; // TODO enum deviceSoftwareVersion: string; - // iOS only + + /** + * iOS Only properties. These properties are not available on Android. + */ isCharging: number; - // Android only + /** + * Android Only properties. These properties are not available on iOS. + */ baseUrl: string; - - // Android only bootloaderVersion: string; - - // Android only configVersion: string; - - // Android only emvKeyProfileId: string; - - // Android only firmwareVersion: string; - - // Android only hardwareVersion: string; - - // Android only macKeyProfileId: string; - - // Android only pinKeyProfileId: string; - - // Android only trackKeyProfileId: string; - - // Android only settingsVersion: string; - - // Android only pinKeysetId: string; /** From adca78fd9d0a885fa736ffe6165dd79fd03703f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Mon, 12 Aug 2024 23:26:57 +0900 Subject: [PATCH 16/24] feat(teminal): set device type --- packages/terminal/README.md | 23 +++++- .../terminal/helper/TerminalMappers.java | 75 ++++++++----------- packages/terminal/src/definitions.ts | 10 ++- packages/terminal/src/stripe.enum.ts | 21 ++++++ 4 files changed, 79 insertions(+), 50 deletions(-) diff --git a/packages/terminal/README.md b/packages/terminal/README.md index f8315649..ffb989a0 100644 --- a/packages/terminal/README.md +++ b/packages/terminal/README.md @@ -803,7 +803,7 @@ addListener(eventName: TerminalEventsEnum.ReaderReconnectFailed, listenerFunc: ( #### ReaderInterface -{ /** * The unique serial number is primary identifier inner plugin. */ serialNumber: string; label: string; batteryLevel: number; batteryStatus: BatteryStatus; simulated: boolean; id: number; availableUpdate: ReaderSoftwareUpdateInterface; locationId: string; ipAddress: string; status: NetworkStatus; location: LocationInterface; locationStatus: LocationStatus; deviceType: string; // TODO enum deviceSoftwareVersion: string; // iOS only isCharging: number; // Android only baseUrl: string; // Android only bootloaderVersion: string; // Android only configVersion: string; // Android only emvKeyProfileId: string; // Android only firmwareVersion: string; // Android only hardwareVersion: string; // Android only macKeyProfileId: string; // Android only pinKeyProfileId: string; // Android only trackKeyProfileId: string; // Android only settingsVersion: string; // Android only pinKeysetId: string; /** * @deprecated This property has been deprecated and should use the `serialNumber` property. */ index?: number; } +{ /** * The unique serial number is primary identifier inner plugin. */ serialNumber: string; label: string; batteryLevel: number; batteryStatus: BatteryStatus; simulated: boolean; id: number; availableUpdate: ReaderSoftwareUpdateInterface; locationId: string; ipAddress: string; status: NetworkStatus; location: LocationInterface; locationStatus: LocationStatus; deviceType: DeviceType; deviceSoftwareVersion: string; /** * iOS Only properties. These properties are not available on Android. */ isCharging: number; /** * Android Only properties. These properties are not available on iOS. */ baseUrl: string; bootloaderVersion: string; configVersion: string; emvKeyProfileId: string; firmwareVersion: string; hardwareVersion: string; macKeyProfileId: string; pinKeyProfileId: string; trackKeyProfileId: string; settingsVersion: string; pinKeysetId: string; /** * @deprecated This property has been deprecated and should use the `serialNumber` property. */ index?: number; } #### ReaderSoftwareUpdateInterface @@ -867,6 +867,27 @@ addListener(eventName: TerminalEventsEnum.ReaderReconnectFailed, listenerFunc: ( | **`Unknown`** | 'UNKNOWN' | +#### DeviceType + +| Members | Value | +| ---------------------- | ------------------------------- | +| **`cotsDevice`** | "cotsDevice" | +| **`wisePad3s`** | "wisePad3s" | +| **`appleBuiltIn`** | "appleBuiltIn" | +| **`chipper1X`** | "chipper1X" | +| **`chipper2X`** | "chipper2X" | +| **`etna`** | "etna" | +| **`stripeM2`** | "stripeM2" | +| **`stripeS700`** | "stripeS700" | +| **`stripeS700DevKit`** | "stripeS700Devkit" | +| **`verifoneP400`** | "verifoneP400" | +| **`wiseCube`** | "wiseCube" | +| **`wisePad3`** | "wisePad3" | +| **`wisePosE`** | "wisePosE" | +| **`wisePosEDevKit`** | "wisePosEDevkit" | +| **`unknown`** | "unknown" | + + #### TerminalConnectTypes | Members | Value | diff --git a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/helper/TerminalMappers.java b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/helper/TerminalMappers.java index 0babcea6..38623649 100644 --- a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/helper/TerminalMappers.java +++ b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/helper/TerminalMappers.java @@ -18,19 +18,19 @@ public JSObject mapFromLocation(Location location) { if (location.getAddress() != null) { address - .put("country", location.getAddress().getCountry()) - .put("city", location.getAddress().getCity()) - .put("postalCode", location.getAddress().getPostalCode()) - .put("line1", location.getAddress().getLine1()) - .put("line2", location.getAddress().getLine2()) - .put("state", location.getAddress().getState()); + .put("country", location.getAddress().getCountry()) + .put("city", location.getAddress().getCity()) + .put("postalCode", location.getAddress().getPostalCode()) + .put("line1", location.getAddress().getLine1()) + .put("line2", location.getAddress().getLine2()) + .put("state", location.getAddress().getState()); } return new JSObject() - .put("id", location.getId()) - .put("displayName", location.getDisplayName()) - .put("address", address) - .put("livemode", location.getLivemode()); + .put("id", location.getId()) + .put("displayName", location.getDisplayName()) + .put("address", address) + .put("livemode", location.getLivemode()); } public JSObject mapFromReaderSoftwareUpdate(ReaderSoftwareUpdate update) { @@ -39,9 +39,9 @@ public JSObject mapFromReaderSoftwareUpdate(ReaderSoftwareUpdate update) { } return new JSObject() - .put("deviceSoftwareVersion", update.getVersion()) - .put("estimatedUpdateTime", update.getTimeEstimate().toString()) - .put("requiredAt", update.getRequiredAt().getTime()); + .put("deviceSoftwareVersion", update.getVersion()) + .put("estimatedUpdateTime", update.getTimeEstimate().toString()) + .put("requiredAt", update.getRequiredAt().getTime()); } public String mapFromLocationStatus(LocationStatus status) { @@ -70,42 +70,27 @@ public String mapFromNetworkStatus(Reader.NetworkStatus status) { } public String mapFromDeviceType(DeviceType type) { - switch (type) { - case CHIPPER_1X: - return "chipper1X"; - case CHIPPER_2X: - return "chipper2X"; - case COTS_DEVICE: - return "cotsDevice"; - case ETNA: - return "etna"; - case STRIPE_M2: - return "stripeM2"; - case STRIPE_S700: - return "stripeS700"; - case STRIPE_S700_DEVKIT: - return "stripeS700Devkit"; + return switch (type) { + case CHIPPER_1X -> "chipper1X"; + case CHIPPER_2X -> "chipper2X"; + case COTS_DEVICE -> "cotsDevice"; + case ETNA -> "etna"; + case STRIPE_M2 -> "stripeM2"; + case STRIPE_S700 -> "stripeS700"; + case STRIPE_S700_DEVKIT -> "stripeS700Devkit"; // React Native has this model. deprecated? // case STRIPE_S710: // return "stripeS710"; // case STRIPE_S710_DEVKIT: // return "stripeS710Devkit"; - case UNKNOWN: - return "unknown"; - case VERIFONE_P400: - return "verifoneP400"; - case WISECUBE: - return "wiseCube"; - case WISEPAD_3: - return "wisePad3"; - case WISEPAD_3S: - return "wisePad3s"; - case WISEPOS_E: - return "wisePosE"; - case WISEPOS_E_DEVKIT: - return "wisePosEDevkit"; - default: - throw new IllegalArgumentException("Unknown DeviceType: " + type); - } + case UNKNOWN -> "unknown"; + case VERIFONE_P400 -> "verifoneP400"; + case WISECUBE -> "wiseCube"; + case WISEPAD_3 -> "wisePad3"; + case WISEPAD_3S -> "wisePad3s"; + case WISEPOS_E -> "wisePosE"; + case WISEPOS_E_DEVKIT -> "wisePosEDevkit"; + default -> throw new IllegalArgumentException("Unknown DeviceType: " + type); + }; } } diff --git a/packages/terminal/src/definitions.ts b/packages/terminal/src/definitions.ts index bfc56dee..c9971442 100644 --- a/packages/terminal/src/definitions.ts +++ b/packages/terminal/src/definitions.ts @@ -1,7 +1,7 @@ import type { PluginListenerHandle } from '@capacitor/core'; import type { TerminalEventsEnum } from './events.enum'; -import { +import type { TerminalConnectTypes, UpdateTimeEstimate, SimulateReaderUpdate, @@ -12,7 +12,10 @@ import { ReaderInputOption, PaymentStatus, DisconnectReason, - ConnectionStatus, NetworkStatus, LocationStatus, + ConnectionStatus, + NetworkStatus, + LocationStatus, + DeviceType, } from './stripe.enum'; export type ReaderInterface = { @@ -32,10 +35,9 @@ export type ReaderInterface = { status: NetworkStatus; location: LocationInterface; locationStatus: LocationStatus; - deviceType: string; // TODO enum + deviceType: DeviceType; deviceSoftwareVersion: string; - /** * iOS Only properties. These properties are not available on Android. */ diff --git a/packages/terminal/src/stripe.enum.ts b/packages/terminal/src/stripe.enum.ts index 3cee3410..497d868b 100644 --- a/packages/terminal/src/stripe.enum.ts +++ b/packages/terminal/src/stripe.enum.ts @@ -6,6 +6,27 @@ export enum TerminalConnectTypes { TapToPay = 'tap-to-pay', } +/** + * Note: Don't need to use this enum. It's just for reference. + */ +export enum DeviceType { + cotsDevice = 'cotsDevice', + wisePad3s = 'wisePad3s', + appleBuiltIn = 'appleBuiltIn', + chipper1X = 'chipper1X', + chipper2X = 'chipper2X', + etna = 'etna', + stripeM2 = 'stripeM2', + stripeS700 = 'stripeS700', + stripeS700DevKit = 'stripeS700Devkit', + verifoneP400 = 'verifoneP400', + wiseCube = 'wiseCube', + wisePad3 = 'wisePad3', + wisePosE = 'wisePosE', + wisePosEDevKit = 'wisePosEDevkit', + unknown = 'unknown', +} + export enum UpdateTimeEstimate { LessThanOneMinute = 'LESS_THAN_ONE_MINUTE', OneToTwoMinutes = 'ONE_TO_TWO_MINUTES', From acedb4033cfd55c6954cf0721dd13be1c247a4b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Tue, 13 Aug 2024 09:16:41 +0900 Subject: [PATCH 17/24] feat(terminal): implement cancelReaderConnection --- .../stripe/terminal/StripeTerminal.java | 30 +++++++++++++++++++ .../stripe/terminal/StripeTerminalPlugin.java | 5 ++++ .../stripe/terminal/TerminalEvent.kt | 1 + .../terminal/ios/Plugin/StripeTerminal.swift | 16 ++++++++++ .../ios/Plugin/StripeTerminalPlugin.m | 1 + .../ios/Plugin/StripeTerminalPlugin.swift | 4 +++ .../terminal/ios/Plugin/TerminalEvents.swift | 1 + packages/terminal/src/definitions.ts | 12 ++++++++ packages/terminal/src/events.enum.ts | 1 + packages/terminal/src/web.ts | 4 +++ 10 files changed, 75 insertions(+) diff --git a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java index de55003a..1a20d2db 100644 --- a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java +++ b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java @@ -69,6 +69,7 @@ public class StripeTerminal extends Executor { private Cancelable discoveryCancelable; private Cancelable collectCancelable; private Cancelable installUpdateCancelable; + private Cancelable cancelReaderConnectionCancellable; private List discoveredReadersList; private String locationId; private PluginCall collectCall; @@ -300,6 +301,15 @@ private void connectLocalMobileReader(final PluginCall call) { } ReaderReconnectionListener readerReconnectionListener = new ReaderReconnectionListener() { + @Override + public void onReaderReconnectStarted(@NonNull Reader reader, @NonNull Cancelable cancelable, @NonNull DisconnectReason reason) { + cancelReaderConnectionCancellable = cancelable; + notifyListeners( + TerminalEnumEvent.ReaderReconnectStarted.getWebEventName(), + new JSObject().put("reason", reason.toString()).put("reader", convertReaderInterface(reader)) + ); + } + @Override public void onReaderReconnectSucceeded(@NonNull Reader reader) { notifyListeners( @@ -610,6 +620,26 @@ public void onFailure(@NonNull TerminalException e) { ); } + public void cancelReaderReconnection(final PluginCall call) { + if (cancelReaderConnectionCancellable != null) { + cancelReaderConnectionCancellable.cancel( + new Callback() { + @Override + public void onSuccess() { + call.resolve(); + } + + @Override + public void onFailure(@NonNull TerminalException ex) { + call.reject(ex.getLocalizedMessage(), ex); + } + } + ); + } else { + call.resolve(); + } + } + private final PaymentIntentCallback confirmPaymentMethodCallback = new PaymentIntentCallback() { @Override public void onSuccess(PaymentIntent paymentIntent) { diff --git a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminalPlugin.java b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminalPlugin.java index 3d9bc698..ba11ce81 100644 --- a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminalPlugin.java +++ b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminalPlugin.java @@ -185,4 +185,9 @@ public void clearReaderDisplay(PluginCall call) { public void rebootReader(PluginCall call) { this.implementation.rebootReader(call); } + + @PluginMethod + public void cancelReaderReconnection(PluginCall call) { + this.implementation.cancelReaderReconnection(call); + } } diff --git a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/TerminalEvent.kt b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/TerminalEvent.kt index 5bc083c3..ed3839a1 100644 --- a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/TerminalEvent.kt +++ b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/TerminalEvent.kt @@ -22,6 +22,7 @@ enum class TerminalEnumEvent(val webEventName: String) { RequestDisplayMessage("terminalRequestDisplayMessage"), RequestReaderInput("terminalRequestReaderInput"), PaymentStatusChange("terminalPaymentStatusChange"), + ReaderReconnectStarted("terminalReaderReconnectStarted"), ReaderReconnectSucceeded("terminalReaderReconnectSucceeded"), ReaderReconnectFailed("terminalReaderReconnectFailed"), } diff --git a/packages/terminal/ios/Plugin/StripeTerminal.swift b/packages/terminal/ios/Plugin/StripeTerminal.swift index 369c4d46..b5a6794b 100644 --- a/packages/terminal/ios/Plugin/StripeTerminal.swift +++ b/packages/terminal/ios/Plugin/StripeTerminal.swift @@ -10,6 +10,7 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg var discoverCancelable: Cancelable? var collectCancelable: Cancelable? var installUpdateCancelable: Cancelable? + var cancelReaderConnectionCancellable: Cancelable? var discoverCall: CAPPluginCall? var locationId: String? @@ -352,6 +353,16 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg } } + public func cancelReaderReconnection(_ call: CAPPluginCall) { + self.cancelReaderConnectionCancellable?.cancel { error in + if let error = error as NSError? { + call.reject(error.localizedDescription) + } else { + call.resolve([:]) + } + } + } + /* * Terminal */ @@ -476,6 +487,11 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg /* * Reconnection */ + public func reader(_ reader: Reader, didStartReconnect cancelable: Cancelable, disconnectReason: DisconnectReason) { + self.cancelReaderConnectionCancellable = cancelable + self.plugin?.notifyListeners(TerminalEvents.ReaderReconnectStarted.rawValue, data: ["reader": self.convertReaderInterface(reader: reader), "reason": disconnectReason.rawValue]) + } + public func readerDidSucceedReconnect(_ reader: Reader) { self.plugin?.notifyListeners(TerminalEvents.ReaderReconnectSucceeded.rawValue, data: ["reader": self.convertReaderInterface(reader: reader)]) } diff --git a/packages/terminal/ios/Plugin/StripeTerminalPlugin.m b/packages/terminal/ios/Plugin/StripeTerminalPlugin.m index c0a9e7ee..e456baed 100644 --- a/packages/terminal/ios/Plugin/StripeTerminalPlugin.m +++ b/packages/terminal/ios/Plugin/StripeTerminalPlugin.m @@ -20,4 +20,5 @@ CAP_PLUGIN_METHOD(setReaderDisplay, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(clearReaderDisplay, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(rebootReader, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(cancelReaderReconnection, CAPPluginReturnPromise); ) diff --git a/packages/terminal/ios/Plugin/StripeTerminalPlugin.swift b/packages/terminal/ios/Plugin/StripeTerminalPlugin.swift index 8ab1a30f..4ec0d682 100644 --- a/packages/terminal/ios/Plugin/StripeTerminalPlugin.swift +++ b/packages/terminal/ios/Plugin/StripeTerminalPlugin.swift @@ -85,4 +85,8 @@ public class StripeTerminalPlugin: CAPPlugin { self.implementation.rebootReader(call) } + @objc func cancelReaderReconnection(_ call: CAPPluginCall) { + self.implementation.cancelReaderReconnection(call) + } + } diff --git a/packages/terminal/ios/Plugin/TerminalEvents.swift b/packages/terminal/ios/Plugin/TerminalEvents.swift index 2cf0554b..a97f7635 100644 --- a/packages/terminal/ios/Plugin/TerminalEvents.swift +++ b/packages/terminal/ios/Plugin/TerminalEvents.swift @@ -19,6 +19,7 @@ public enum TerminalEvents: String { case RequestDisplayMessage = "terminalRequestDisplayMessage" case RequestReaderInput = "terminalRequestReaderInput" case PaymentStatusChange = "terminalPaymentStatusChange" + case ReaderReconnectStarted = "terminalReaderReconnectStarted" case ReaderReconnectSucceeded = "terminalReaderReconnectSucceeded" case ReaderReconnectFailed = "terminalReaderReconnectFailed" } diff --git a/packages/terminal/src/definitions.ts b/packages/terminal/src/definitions.ts index c9971442..f14d548f 100644 --- a/packages/terminal/src/definitions.ts +++ b/packages/terminal/src/definitions.ts @@ -137,6 +137,7 @@ export interface StripeTerminalPlugin { setReaderDisplay(options: Cart): Promise; clearReaderDisplay(): Promise; rebootReader(): Promise; + cancelReaderReconnection(): Promise; addListener( eventName: TerminalEventsEnum.Loaded, @@ -386,6 +387,17 @@ export interface StripeTerminalPlugin { listenerFunc: ({ status }: { status: PaymentStatus }) => void, ): Promise; + addListener( + eventName: TerminalEventsEnum.ReaderReconnectStarted, + listenerFunc: ({ + reader, + reason, + }: { + reader: ReaderInterface; + reason: string; + }) => void, + ): Promise; + addListener( eventName: TerminalEventsEnum.ReaderReconnectSucceeded, listenerFunc: ({ reader }: { reader: ReaderInterface }) => void, diff --git a/packages/terminal/src/events.enum.ts b/packages/terminal/src/events.enum.ts index 07e9223d..51725eb2 100644 --- a/packages/terminal/src/events.enum.ts +++ b/packages/terminal/src/events.enum.ts @@ -20,6 +20,7 @@ export enum TerminalEventsEnum { RequestDisplayMessage = 'terminalRequestDisplayMessage', RequestReaderInput = 'terminalRequestReaderInput', PaymentStatusChange = 'terminalPaymentStatusChange', + ReaderReconnectStarted = 'terminalReaderReconnectStarted', ReaderReconnectSucceeded = 'terminalReaderReconnectSucceeded', ReaderReconnectFailed = 'terminalReaderReconnectFailed', } diff --git a/packages/terminal/src/web.ts b/packages/terminal/src/web.ts index f561bacc..31602c86 100644 --- a/packages/terminal/src/web.ts +++ b/packages/terminal/src/web.ts @@ -103,6 +103,10 @@ export class StripeTerminalWeb console.log('rebootReader'); } + async cancelReaderReconnection(): Promise { + console.log('cancelReaderReconnection'); + } + collect = 'deprecated'; cancelCollect = 'deprecated'; } From 6c28b80183c1ce78a2d991d80f90a2618afce23a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Tue, 13 Aug 2024 09:52:32 +0900 Subject: [PATCH 18/24] feat(terminal): organize cancelable --- .../stripe/terminal/StripeTerminal.java | 66 +++++---- .../terminal/ios/Plugin/StripeTerminal.swift | 125 +++++++++++------- .../ios/Plugin/StripeTerminalPlugin.swift | 4 - 3 files changed, 107 insertions(+), 88 deletions(-) diff --git a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java index 1a20d2db..e7e9a84a 100644 --- a/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java +++ b/packages/terminal/android/src/main/java/com/getcapacitor/community/stripe/terminal/StripeTerminal.java @@ -381,24 +381,24 @@ private void connectBluetoothReader(final PluginCall call) { } public void cancelDiscoverReaders(final PluginCall call) { - if (discoveryCancelable != null) { - discoveryCancelable.cancel( - new Callback() { - @Override - public void onSuccess() { - notifyListeners(TerminalEnumEvent.CancelDiscoveredReaders.getWebEventName(), emptyObject); - call.resolve(); - } - - @Override - public void onFailure(@NonNull TerminalException ex) { - call.reject(ex.getLocalizedMessage(), ex); - } - } - ); - } else { + if (discoveryCancelable == null || discoveryCancelable.isCompleted()) { call.resolve(); + return; } + discoveryCancelable.cancel( + new Callback() { + @Override + public void onSuccess() { + notifyListeners(TerminalEnumEvent.CancelDiscoveredReaders.getWebEventName(), emptyObject); + call.resolve(); + } + + @Override + public void onFailure(@NonNull TerminalException ex) { + call.reject(ex.getLocalizedMessage(), ex); + } + } + ); } public void collectPaymentMethod(final PluginCall call) { @@ -420,7 +420,6 @@ public void cancelCollectPaymentMethod(final PluginCall call) { new Callback() { @Override public void onSuccess() { - collectCancelable = null; notifyListeners(TerminalEnumEvent.Canceled.getWebEventName(), emptyObject); call.resolve(); } @@ -450,7 +449,6 @@ public void onFailure(@NonNull TerminalException ex) { private final PaymentIntentCallback collectPaymentMethodCallback = new PaymentIntentCallback() { @Override public void onSuccess(PaymentIntent paymentIntent) { - collectCancelable = null; paymentIntentInstance = paymentIntent; notifyListeners(TerminalEnumEvent.CollectedPaymentIntent.getWebEventName(), emptyObject); @@ -480,7 +478,6 @@ public void onSuccess(PaymentIntent paymentIntent) { @Override public void onFailure(@NonNull TerminalException ex) { - collectCancelable = null; notifyListeners(TerminalEnumEvent.Failed.getWebEventName(), emptyObject); collectCall.reject(ex.getLocalizedMessage(), ex); } @@ -511,7 +508,6 @@ public void cancelInstallUpdate(final PluginCall call) { new Callback() { @Override public void onSuccess() { - installUpdateCancelable = null; call.resolve(); } @@ -621,23 +617,23 @@ public void onFailure(@NonNull TerminalException e) { } public void cancelReaderReconnection(final PluginCall call) { - if (cancelReaderConnectionCancellable != null) { - cancelReaderConnectionCancellable.cancel( - new Callback() { - @Override - public void onSuccess() { - call.resolve(); - } - - @Override - public void onFailure(@NonNull TerminalException ex) { - call.reject(ex.getLocalizedMessage(), ex); - } - } - ); - } else { + if (cancelReaderConnectionCancellable == null || cancelReaderConnectionCancellable.isCompleted()) { call.resolve(); + return; } + cancelReaderConnectionCancellable.cancel( + new Callback() { + @Override + public void onSuccess() { + call.resolve(); + } + + @Override + public void onFailure(@NonNull TerminalException ex) { + call.reject(ex.getLocalizedMessage(), ex); + } + } + ); } private final PaymentIntentCallback confirmPaymentMethodCallback = new PaymentIntentCallback() { diff --git a/packages/terminal/ios/Plugin/StripeTerminal.swift b/packages/terminal/ios/Plugin/StripeTerminal.swift index b5a6794b..8d956f42 100644 --- a/packages/terminal/ios/Plugin/StripeTerminal.swift +++ b/packages/terminal/ios/Plugin/StripeTerminal.swift @@ -65,27 +65,11 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg call.reject(error.localizedDescription) self.discoverCall = nil } else { + // This call is passed to discoverCall. So not resolve. } } } - func cancelDiscoverReaders(_ call: CAPPluginCall) { - - if let cancelable = self.discoverCancelable { - cancelable.cancel { error in - if let error = error { - call.reject(error.localizedDescription) - } else { - self.collectCancelable = nil - call.resolve() - } - } - return - } - - call.resolve() - } - public func terminal(_ terminal: Terminal, didUpdateDiscoveredReaders readers: [Reader]) { var readersJSObject: JSArray = [] var i = 0 @@ -217,6 +201,7 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg Terminal.shared.retrievePaymentIntent(clientSecret: call.getString("paymentIntent")!) { retrieveResult, retrieveError in if let error = retrieveError { print("retrievePaymentIntent failed: \(error)") + call.reject(error.localizedDescription) } else if let paymentIntent = retrieveResult { self.collectCancelable = Terminal.shared.collectPaymentMethod(paymentIntent) { collectResult, collectError in if let error = collectError { @@ -232,23 +217,6 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg } } - public func cancelCollectPaymentMethod(_ call: CAPPluginCall) { - if let cancelable = self.collectCancelable { - cancelable.cancel { error in - if let error = error { - call.reject(error.localizedDescription) - } else { - self.plugin?.notifyListeners(TerminalEvents.Canceled.rawValue, data: [:]) - self.collectCancelable = nil - self.paymentIntent = nil - call.resolve() - } - } - return - } - call.resolve() - } - public func confirmPaymentIntent(_ call: CAPPluginCall) { if let paymentIntent = self.paymentIntent { Terminal.shared.confirmPaymentIntent(paymentIntent) { confirmResult, confirmError in @@ -282,16 +250,6 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg call.resolve([:]) } - public func cancelInstallUpdate(_ call: CAPPluginCall) { - self.installUpdateCancelable?.cancel { error in - if let error = error as NSError? { - call.reject(error.localizedDescription) - } else { - call.resolve([:]) - } - } - } - public func setReaderDisplay(_ call: CAPPluginCall) { guard let currency = call.getString("currency") else { call.reject("You must provide a currency value") @@ -353,14 +311,83 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg } } + /** + * Cancelable + */ + public func cancelInstallUpdate(_ call: CAPPluginCall) { + if let cancelable = self.installUpdateCancelable { + if cancelable.completed { + call.resolve() + return + } + cancelable.cancel { error in + if let error = error as NSError? { + call.reject(error.localizedDescription) + } else { + call.resolve([:]) + } + } + return + } + call.resolve([:]) + } + + public func cancelCollectPaymentMethod(_ call: CAPPluginCall) { + if let cancelable = self.collectCancelable { + if cancelable.completed { + call.resolve() + return + } + cancelable.cancel { error in + if let error = error { + call.reject(error.localizedDescription) + } else { + self.plugin?.notifyListeners(TerminalEvents.Canceled.rawValue, data: [:]) + self.paymentIntent = nil + call.resolve() + } + } + return + } + call.resolve() + } + + func cancelDiscoverReaders(_ call: CAPPluginCall) { + if let cancelable = self.discoverCancelable { + if cancelable.completed { + call.resolve() + return + } + cancelable.cancel { error in + if let error = error { + call.reject(error.localizedDescription) + } else { + call.resolve() + } + } + return + } + + call.resolve() + } + public func cancelReaderReconnection(_ call: CAPPluginCall) { - self.cancelReaderConnectionCancellable?.cancel { error in - if let error = error as NSError? { - call.reject(error.localizedDescription) - } else { - call.resolve([:]) + if let cancelable = self.cancelReaderConnectionCancellable { + if cancelable.completed { + call.resolve() + return } + cancelable.cancel { error in + if let error = error as NSError? { + call.reject(error.localizedDescription) + } else { + call.resolve([:]) + } + } + return } + + call.resolve() } /* diff --git a/packages/terminal/ios/Plugin/StripeTerminalPlugin.swift b/packages/terminal/ios/Plugin/StripeTerminalPlugin.swift index 4ec0d682..b5637a38 100644 --- a/packages/terminal/ios/Plugin/StripeTerminalPlugin.swift +++ b/packages/terminal/ios/Plugin/StripeTerminalPlugin.swift @@ -3,10 +3,6 @@ import StripeTerminal import Capacitor import PassKit -/** - * Please read the Capacitor iOS Plugin Development Guide - * here: https://capacitorjs.com/docs/plugins/ios - */ @objc(StripeTerminalPlugin) public class StripeTerminalPlugin: CAPPlugin { private let implementation = StripeTerminal() From 9aed8533a75ab01e539688a75c339dc18409b96a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Tue, 13 Aug 2024 10:21:44 +0900 Subject: [PATCH 19/24] feat(terminal): add merhantDisplayName and behalfOf for localMobile --- packages/terminal/ios/Plugin/StripeTerminal.swift | 7 +++++++ packages/terminal/src/definitions.ts | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/packages/terminal/ios/Plugin/StripeTerminal.swift b/packages/terminal/ios/Plugin/StripeTerminal.swift index 8d956f42..0fd9d609 100644 --- a/packages/terminal/ios/Plugin/StripeTerminal.swift +++ b/packages/terminal/ios/Plugin/StripeTerminal.swift @@ -126,10 +126,14 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg private func connectLocalMobileReader(_ call: CAPPluginCall) { let autoReconnectOnUnexpectedDisconnect = call.getBool("autoReconnectOnUnexpectedDisconnect", false) + let merchantDisplayName: String? = call.getString("merchantDisplayName"); + let onBehalfOf: String? = call.getString("onBehalfOf"); let reader: JSObject = call.getObject("reader")! let serialNumber: String = reader["serialNumber"] as! String let connectionConfig = try! LocalMobileConnectionConfigurationBuilder.init(locationId: self.locationId!) + .setMerchantDisplayName(merchantDisplayName ?? nil) + .setOnBehalfOf(onBehalfOf ?? nil) .setAutoReconnectOnUnexpectedDisconnect(autoReconnectOnUnexpectedDisconnect) .setAutoReconnectionDelegate(autoReconnectOnUnexpectedDisconnect ? self : nil) .build() @@ -182,6 +186,9 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg } let autoReconnectOnUnexpectedDisconnect = call.getBool("autoReconnectOnUnexpectedDisconnect", false) + let merchantDisplayName: String? = call.getString("merchantDisplayName"); + let onBehalfOf: String? = call.getString("onBehalfOf"); + let config = try! BluetoothConnectionConfigurationBuilder(locationId: self.locationId!) .setAutoReconnectOnUnexpectedDisconnect(autoReconnectOnUnexpectedDisconnect) .setAutoReconnectionDelegate(autoReconnectOnUnexpectedDisconnect ? self : nil) diff --git a/packages/terminal/src/definitions.ts b/packages/terminal/src/definitions.ts index f14d548f..a1db543e 100644 --- a/packages/terminal/src/definitions.ts +++ b/packages/terminal/src/definitions.ts @@ -125,6 +125,17 @@ export interface StripeTerminalPlugin { connectReader(options: { reader: ReaderInterface; autoReconnectOnUnexpectedDisconnect?: boolean; + + /** + * iOS and LocalMobileReader only. Android needs to be set to PaymentIntent only. + */ + merchantDisplayName?: string; + + /** + * iOS and LocalMobileReader only. Android needs to be set to PaymentIntent only. + * The Stripe account ID for which these funds are intended. + */ + onBehalfOf?: string; }): Promise; getConnectedReader(): Promise<{ reader: ReaderInterface | null }>; disconnectReader(): Promise; From d46777780c68b722c370532e320274dbbf2c1cb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Tue, 13 Aug 2024 10:53:52 +0900 Subject: [PATCH 20/24] update readme --- packages/terminal/README.md | 168 ++++++++++++++++++++++++++++++------ 1 file changed, 141 insertions(+), 27 deletions(-) diff --git a/packages/terminal/README.md b/packages/terminal/README.md index ffb989a0..77f7f867 100644 --- a/packages/terminal/README.md +++ b/packages/terminal/README.md @@ -1,13 +1,8 @@ # @capacitor-community/stripe-terminal -Stripe SDK bindings for Capacitor Applications. __This plugin is still in beta.__ +Stripe SDK bindings for Capacitor Applications. __This plugin is still in rc(pre-release) version.__ We have confirmed that it works well in the demo project. Please refer to https://github.com/capacitor-community/stripe/tree/main/demo/angular for the implementation. -- [x] Tap To Pay -- [x] Internet -- [x] Bluetooth -- [x] USB - ## Install ```bash @@ -57,7 +52,9 @@ And update minSdkVersion to 26 And compileSdkVersion to 34 in your `android/app/ ## Usage -### use native http client for getting a token +### Simple collect payment + +#### Use plugin client ```typescript (async ()=> { @@ -81,7 +78,7 @@ And update minSdkVersion to 26 And compileSdkVersion to 34 in your `android/app/ }); ``` -### set string token +#### set string token ```typescript (async ()=> { @@ -109,6 +106,95 @@ And update minSdkVersion to 26 And compileSdkVersion to 34 in your `android/app/ }); ```` +### Listen device update + +The device will **if necessary** automatically start updating itself. It is important to handle them as needed so as not to disrupt business operations. + +```ts +(async ()=> { + StripeTerminal.addListener(TerminalEventsEnum.ReportAvailableUpdate, async ({ update }) => { + if (window.confirm("Will you update the device?")) { + await StripeTerminal.installAvailableUpdate(); + } + }); + StripeTerminal.addListener(TerminalEventsEnum.StartInstallingUpdate, async ({ update }) => { + console.log(update); + if (window.confirm("Will you interrupt the update?")) { + StripeTerminal.cancelInstallUpdate(); + } + }); + StripeTerminal.addListener(TerminalEventsEnum.ReaderSoftwareUpdateProgress, async ({ progress }) => { + // be able to use this value to create a progress bar. + }); + StripeTerminal.addListener(TerminalEventsEnum.FinishInstallingUpdate, async ({ update }) => { + console.log(update); + }); +}); +``` + +### Get terminal processing information + +For devices without leader screen, processing information must be retrieved and displayed on the mobile device. Get it with a listener. + +```ts +/** + * Listen battery level. If the battery level is low, you can notify the user to charge the device. + */ +StripeTerminal.addListener(TerminalEventsEnum.BatteryLevel, async ({ level, charging, status }) => { + console.log(level, charging, status); +}); + +/** + * Listen reader event. You can get the reader's status and display it on the mobile device. + */ +StripeTerminal.addListener(TerminalEventsEnum.ReaderEvent, async ({ event }) => { + console.log(event); +}); + +/** + * Listen display message. You can get the message to be displayed on the mobile device. + */ +StripeTerminal.addListener(TerminalEventsEnum.RequestDisplayMessage, async ({ messageType, message }) => { + console.log(messageType, message); +}); + +/** + * Listen reader input. You can get the message what can be used for payment. + */ +StripeTerminal.addListener(TerminalEventsEnum.RequestReaderInput, async ({ options, message }) => { + console.log(options, message); +}); +``` + +### More details on the leader screen + +The contents of the payment can be shown on the display. This requires a leader screen on the device. +This should be run before `collectPaymentMethod`. + +```ts +await StripeTerminal.setReaderDisplay({ + currency: 'usd', + tax: 0, + total: 1000, + lineItems: [{ + displayName: 'winecode', + quantity: 2, + amount: 500 + }] as CartLineItem[], +}) + +// Of course, erasure is also possible. +await StripeTerminal.clearReaderDisplay(); +``` + +### Simulate reader status changes for testing + +To implement updates, etc., we are facilitating an API to change the state of the simulator. This should be done before discoverReaders. + +```ts +await StripeTerminal.setSimulatorConfiguration({ update: SimulateReaderUpdate.UpdateAvailable }) +``` + ## API @@ -129,6 +215,7 @@ And update minSdkVersion to 26 And compileSdkVersion to 34 in your `android/app/ * [`setReaderDisplay(...)`](#setreaderdisplay) * [`clearReaderDisplay()`](#clearreaderdisplay) * [`rebootReader()`](#rebootreader) +* [`cancelReaderReconnection()`](#cancelreaderreconnection) * [`addListener(TerminalEventsEnum.Loaded, ...)`](#addlistenerterminaleventsenumloaded) * [`addListener(TerminalEventsEnum.RequestedConnectionToken, ...)`](#addlistenerterminaleventsenumrequestedconnectiontoken) * [`addListener(TerminalEventsEnum.DiscoveredReaders, ...)`](#addlistenerterminaleventsenumdiscoveredreaders) @@ -149,6 +236,7 @@ And update minSdkVersion to 26 And compileSdkVersion to 34 in your `android/app/ * [`addListener(TerminalEventsEnum.RequestDisplayMessage, ...)`](#addlistenerterminaleventsenumrequestdisplaymessage) * [`addListener(TerminalEventsEnum.RequestReaderInput, ...)`](#addlistenerterminaleventsenumrequestreaderinput) * [`addListener(TerminalEventsEnum.PaymentStatusChange, ...)`](#addlistenerterminaleventsenumpaymentstatuschange) +* [`addListener(TerminalEventsEnum.ReaderReconnectStarted, ...)`](#addlistenerterminaleventsenumreaderreconnectstarted) * [`addListener(TerminalEventsEnum.ReaderReconnectSucceeded, ...)`](#addlistenerterminaleventsenumreaderreconnectsucceeded) * [`addListener(TerminalEventsEnum.ReaderReconnectFailed, ...)`](#addlistenerterminaleventsenumreaderreconnectfailed) * [Interfaces](#interfaces) @@ -219,12 +307,12 @@ setSimulatorConfiguration(options: { update?: SimulateReaderUpdate; simulatedCar ### connectReader(...) ```typescript -connectReader(options: { reader: ReaderInterface; autoReconnectOnUnexpectedDisconnect?: boolean; }) => Promise +connectReader(options: { reader: ReaderInterface; autoReconnectOnUnexpectedDisconnect?: boolean; merchantDisplayName?: string; onBehalfOf?: string; }) => Promise ``` -| Param | Type | -| ------------- | ----------------------------------------------------------------------------------------------------------------------- | -| **`options`** | { reader: ReaderInterface; autoReconnectOnUnexpectedDisconnect?: boolean; } | +| Param | Type | +| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **`options`** | { reader: ReaderInterface; autoReconnectOnUnexpectedDisconnect?: boolean; merchantDisplayName?: string; onBehalfOf?: string; } | -------------------- @@ -338,6 +426,15 @@ rebootReader() => Promise -------------------- +### cancelReaderReconnection() + +```typescript +cancelReaderReconnection() => Promise +``` + +-------------------- + + ### addListener(TerminalEventsEnum.Loaded, ...) ```typescript @@ -756,6 +853,22 @@ addListener(eventName: TerminalEventsEnum.PaymentStatusChange, listenerFunc: ({ -------------------- +### addListener(TerminalEventsEnum.ReaderReconnectStarted, ...) + +```typescript +addListener(eventName: TerminalEventsEnum.ReaderReconnectStarted, listenerFunc: ({ reader, reason, }: { reader: ReaderInterface; reason: string; }) => void) => Promise +``` + +| Param | Type | +| ------------------ | -------------------------------------------------------------------------------------------------------------------------- | +| **`eventName`** | TerminalEventsEnum.ReaderReconnectStarted | +| **`listenerFunc`** | ({ reader, reason, }: { reader: ReaderInterface; reason: string; }) => void | + +**Returns:** Promise<PluginListenerHandle> + +-------------------- + + ### addListener(TerminalEventsEnum.ReaderReconnectSucceeded, ...) ```typescript @@ -871,21 +984,21 @@ addListener(eventName: TerminalEventsEnum.ReaderReconnectFailed, listenerFunc: ( | Members | Value | | ---------------------- | ------------------------------- | -| **`cotsDevice`** | "cotsDevice" | -| **`wisePad3s`** | "wisePad3s" | -| **`appleBuiltIn`** | "appleBuiltIn" | -| **`chipper1X`** | "chipper1X" | -| **`chipper2X`** | "chipper2X" | -| **`etna`** | "etna" | -| **`stripeM2`** | "stripeM2" | -| **`stripeS700`** | "stripeS700" | -| **`stripeS700DevKit`** | "stripeS700Devkit" | -| **`verifoneP400`** | "verifoneP400" | -| **`wiseCube`** | "wiseCube" | -| **`wisePad3`** | "wisePad3" | -| **`wisePosE`** | "wisePosE" | -| **`wisePosEDevKit`** | "wisePosEDevkit" | -| **`unknown`** | "unknown" | +| **`cotsDevice`** | 'cotsDevice' | +| **`wisePad3s`** | 'wisePad3s' | +| **`appleBuiltIn`** | 'appleBuiltIn' | +| **`chipper1X`** | 'chipper1X' | +| **`chipper2X`** | 'chipper2X' | +| **`etna`** | 'etna' | +| **`stripeM2`** | 'stripeM2' | +| **`stripeS700`** | 'stripeS700' | +| **`stripeS700DevKit`** | 'stripeS700Devkit' | +| **`verifoneP400`** | 'verifoneP400' | +| **`wiseCube`** | 'wiseCube' | +| **`wisePad3`** | 'wisePad3' | +| **`wisePosE`** | 'wisePosE' | +| **`wisePosEDevKit`** | 'wisePosEDevkit' | +| **`unknown`** | 'unknown' | #### TerminalConnectTypes @@ -970,6 +1083,7 @@ addListener(eventName: TerminalEventsEnum.ReaderReconnectFailed, listenerFunc: ( | **`RequestDisplayMessage`** | 'terminalRequestDisplayMessage' | | **`RequestReaderInput`** | 'terminalRequestReaderInput' | | **`PaymentStatusChange`** | 'terminalPaymentStatusChange' | +| **`ReaderReconnectStarted`** | 'terminalReaderReconnectStarted' | | **`ReaderReconnectSucceeded`** | 'terminalReaderReconnectSucceeded' | | **`ReaderReconnectFailed`** | 'terminalReaderReconnectFailed' | From c48845af2eff6435f12b3348fb91a32dce145cdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Tue, 13 Aug 2024 10:55:04 +0900 Subject: [PATCH 21/24] fmt --- packages/terminal/ios/Plugin/StripeTerminal.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/terminal/ios/Plugin/StripeTerminal.swift b/packages/terminal/ios/Plugin/StripeTerminal.swift index 0fd9d609..136d31ad 100644 --- a/packages/terminal/ios/Plugin/StripeTerminal.swift +++ b/packages/terminal/ios/Plugin/StripeTerminal.swift @@ -126,8 +126,8 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg private func connectLocalMobileReader(_ call: CAPPluginCall) { let autoReconnectOnUnexpectedDisconnect = call.getBool("autoReconnectOnUnexpectedDisconnect", false) - let merchantDisplayName: String? = call.getString("merchantDisplayName"); - let onBehalfOf: String? = call.getString("onBehalfOf"); + let merchantDisplayName: String? = call.getString("merchantDisplayName") + let onBehalfOf: String? = call.getString("onBehalfOf") let reader: JSObject = call.getObject("reader")! let serialNumber: String = reader["serialNumber"] as! String @@ -186,9 +186,9 @@ public class StripeTerminal: NSObject, DiscoveryDelegate, LocalMobileReaderDeleg } let autoReconnectOnUnexpectedDisconnect = call.getBool("autoReconnectOnUnexpectedDisconnect", false) - let merchantDisplayName: String? = call.getString("merchantDisplayName"); - let onBehalfOf: String? = call.getString("onBehalfOf"); - + let merchantDisplayName: String? = call.getString("merchantDisplayName") + let onBehalfOf: String? = call.getString("onBehalfOf") + let config = try! BluetoothConnectionConfigurationBuilder(locationId: self.locationId!) .setAutoReconnectOnUnexpectedDisconnect(autoReconnectOnUnexpectedDisconnect) .setAutoReconnectionDelegate(autoReconnectOnUnexpectedDisconnect ? self : nil) From 60a0c8caf413bbfc1abb11ec47327b219e8b1529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Tue, 13 Aug 2024 11:46:29 +0900 Subject: [PATCH 22/24] fix(demo): cancelCollectPaymentMethod set at during collectPaymentMethod --- .../angular/src/app/terminal/terminal.page.ts | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/demo/angular/src/app/terminal/terminal.page.ts b/demo/angular/src/app/terminal/terminal.page.ts index bff532ee..81a73293 100644 --- a/demo/angular/src/app/terminal/terminal.page.ts +++ b/demo/angular/src/app/terminal/terminal.page.ts @@ -165,20 +165,20 @@ export class TerminalPage { await StripeTerminal.clearReaderDisplay(); } - await StripeTerminal.collectPaymentMethod({ paymentIntent }) - .then(() => - this.helper.updateItem(this.eventItems, 'collectPaymentMethod', true), - ) - .catch(async (e) => { - await this.helper.updateItem( - this.eventItems, - 'collectPaymentMethod', - false, - ); - throw e; - }); + if (type === 'cancelPath') { + // During Collect, cancel the payment + StripeTerminal.collectPaymentMethod({ paymentIntent }) + .catch(async (e) => { + await this.helper.updateItem( + this.eventItems, + 'collectPaymentMethod', + false, + ); + throw e; + }); + await this.helper.updateItem(this.eventItems, 'collectPaymentMethod', true); await new Promise((resolve) => setTimeout(resolve, 2000)); await StripeTerminal.cancelCollectPaymentMethod().catch(async (e) => { await this.helper.updateItem( @@ -194,6 +194,18 @@ export class TerminalPage { true, ); } else { + await StripeTerminal.collectPaymentMethod({ paymentIntent }) + .then(() => + this.helper.updateItem(this.eventItems, 'collectPaymentMethod', true), + ) + .catch(async (e) => { + await this.helper.updateItem( + this.eventItems, + 'collectPaymentMethod', + false, + ); + throw e; + }); await StripeTerminal.confirmPaymentIntent(); await this.helper.updateItem( this.eventItems, From 11b47ca5228c555581170026e6456cde2a423882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Tue, 13 Aug 2024 12:02:41 +0900 Subject: [PATCH 23/24] chore(terminal): add enum group by device image --- packages/terminal/src/stripe.enum.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/terminal/src/stripe.enum.ts b/packages/terminal/src/stripe.enum.ts index 497d868b..5adba9db 100644 --- a/packages/terminal/src/stripe.enum.ts +++ b/packages/terminal/src/stripe.enum.ts @@ -27,6 +27,28 @@ export enum DeviceType { unknown = 'unknown', } +/** + * This group is useful for pick image. + * Reference: https://github.com/stripe/stripe-terminal-ios/blob/fc571ab441b14639243a11d19d8f62bbe93feea5/Example/Example/ReaderHeaderView.swift#L95-L113 + */ +export enum DeviceGroup { + stripeM2 = 'stripe_m2', + chipper1X = 'chipper', + chipper2X = 'chipper', + wiseCube = 'chipper', + verifoneP400 = 'verifone', + wisePad3s = 'wisepad', + wisePad3 = 'wisepad', + wisePosEDevKit = 'wisepose', + etna = 'wisepose', + wisePosE = 'wisepose', + stripeS700DevKit = 's700', + stripeS700 = 's700', + appleBuiltIn = 'apple', // unknown change to apple + cotsDevice = 'unknown', + unknown = 'unknown', +} + export enum UpdateTimeEstimate { LessThanOneMinute = 'LESS_THAN_ONE_MINUTE', OneToTwoMinutes = 'ONE_TO_TWO_MINUTES', From 4680e31e09abb4096f0b7d2419a6631b15bedcbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Tue, 13 Aug 2024 12:08:34 +0900 Subject: [PATCH 24/24] chore(demo): update that select device is arable only --- demo/angular/src/app/terminal/terminal.page.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/demo/angular/src/app/terminal/terminal.page.ts b/demo/angular/src/app/terminal/terminal.page.ts index 81a73293..3f902fc9 100644 --- a/demo/angular/src/app/terminal/terminal.page.ts +++ b/demo/angular/src/app/terminal/terminal.page.ts @@ -453,12 +453,14 @@ export class TerminalPage { const alert = await this.alertCtrl.create({ header: `Select a reader`, message: `Select a reader to connect to.`, + backdropDismiss: false, inputs: readers.map((reader, index) => ({ name: 'serialNumber', type: 'radio', - label: reader.serialNumber, + label: reader.deviceType, value: reader.serialNumber, checked: index === 0, + disabled: reader.status === 'OFFLINE' })), buttons: [ {