@capawesome/capacitor-square-mobile-payments 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Package.swift CHANGED
@@ -22,10 +22,6 @@ let package = Package(
22
22
  .product(name: "SquareMobilePaymentsSDK", package: "mobile-payments-sdk-ios"),
23
23
  .product(name: "MockReaderUI", package: "mobile-payments-sdk-ios")
24
24
  ],
25
- path: "ios/Plugin"),
26
- .testTarget(
27
- name: "SquareMobilePaymentsPluginTests",
28
- dependencies: ["SquareMobilePaymentsPlugin"],
29
- path: "ios/PluginTests")
25
+ path: "ios/Plugin")
30
26
  ]
31
27
  )
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @capawesome/capacitor-square-mobile-payments
1
+ # Capacitor Square Mobile Payments Plugin
2
2
 
3
3
  Unofficial Capacitor plugin for [Square Mobile Payments SDK](https://developer.squareup.com/docs/mobile-payments-sdk).[^1]
4
4
 
@@ -25,6 +25,16 @@ This plugin provides a comprehensive integration with Square's Mobile Payments S
25
25
 
26
26
  Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look!
27
27
 
28
+ ## Use Cases
29
+
30
+ The Square Mobile Payments plugin is typically used to accept in-person payments in a Capacitor app, for example:
31
+
32
+ - **Point of sale**: Turn your app into a POS system that accepts tap, dip, swipe, and manually entered card payments with Square card readers.
33
+ - **Tap to Pay on iPhone**: Accept contactless payments directly on an iPhone without additional hardware by linking a Square seller account with an Apple ID.
34
+ - **Mobile and pop-up sales**: Pair, monitor, and manage Square readers for markets, food trucks, or events, and process payments offline with automatic sync when connectivity is limited.
35
+ - **Compliant receipts**: Access card details, authorization codes, and EMV data of completed payments to generate compliant receipts.
36
+ - **Testing without hardware**: Use the mock reader in debug builds to test the whole payment flow without a physical reader.
37
+
28
38
  ## Compatibility
29
39
 
30
40
  | Plugin Version | Capacitor Version | Status |
@@ -33,6 +43,21 @@ Missing a feature? Just [open an issue](https://github.com/capawesome-team/capac
33
43
 
34
44
  ## Installation
35
45
 
46
+ You can use our **AI-Assisted Setup** to install the plugin.
47
+ Add the [Capawesome Skills](https://github.com/capawesome-team/skills) to your AI tool using the following command:
48
+
49
+ ```bash
50
+ npx skills add capawesome-team/skills --skill capacitor-plugins
51
+ ```
52
+
53
+ Then use the following prompt:
54
+
55
+ ```
56
+ Use the `capacitor-plugins` skill from `capawesome-team/skills` to install the `@capawesome/capacitor-square-mobile-payments` plugin in my project.
57
+ ```
58
+
59
+ If you prefer **Manual Setup**, install the plugin by running the following commands and follow the platform-specific instructions below:
60
+
36
61
  ```bash
37
62
  npm install @capawesome/capacitor-square-mobile-payments
38
63
  npx cap sync
@@ -40,6 +65,38 @@ npx cap sync
40
65
 
41
66
  ### Android
42
67
 
68
+ #### SDK Initialization
69
+
70
+ The Square Mobile Payments SDK must be initialized in your `Application` class before using the plugin. Create a custom `Application` class (if you don't already have one) and add the following code:
71
+
72
+ 1. Create a file `MainApplication.java` in your app's `android/app/src/main/java/<your-package>/` directory:
73
+
74
+ ```java
75
+ package com.example.app;
76
+
77
+ import android.app.Application;
78
+ import com.squareup.sdk.mobilepayments.MobilePaymentsSdk;
79
+
80
+ public class MainApplication extends Application {
81
+
82
+ @Override
83
+ public void onCreate() {
84
+ super.onCreate();
85
+ MobilePaymentsSdk.initialize("YOUR_SQUARE_APPLICATION_ID", this);
86
+ }
87
+ }
88
+ ```
89
+
90
+ **Note**: Replace `YOUR_SQUARE_APPLICATION_ID` with your actual Square Application ID.
91
+
92
+ 2. Register the custom `Application` class in your `AndroidManifest.xml`:
93
+
94
+ ```xml
95
+ <application
96
+ android:name=".MainApplication"
97
+ ...>
98
+ ```
99
+
43
100
  #### Variables
44
101
 
45
102
  If needed, you can define the following project variable in your app's `variables.gradle` file to change the default version of the dependency:
@@ -122,8 +179,14 @@ No configuration required for this plugin.
122
179
 
123
180
  ## Usage
124
181
 
182
+ The following examples show how to initialize and authorize the SDK, pair and list readers, start a payment, read the available card input methods, and listen for payment and reader events.
183
+
184
+ ### Initialize and authorize the SDK
185
+
186
+ Initialize the SDK with your Square location ID and authorize it with a Square access token. The `initialize(...)` method must be called before any other method:
187
+
125
188
  ```typescript
126
- import { SquareMobilePayments, CardInputMethod } from '@capawesome/capacitor-square-mobile-payments';
189
+ import { SquareMobilePayments } from '@capawesome/capacitor-square-mobile-payments';
127
190
 
128
191
  const initializeSDK = async () => {
129
192
  await SquareMobilePayments.initialize({
@@ -134,15 +197,39 @@ const initializeSDK = async () => {
134
197
  accessToken: 'YOUR_ACCESS_TOKEN',
135
198
  });
136
199
  };
200
+ ```
201
+
202
+ ### Check the authorization state
203
+
204
+ Check whether the SDK is currently authorized, for example on app start:
205
+
206
+ ```typescript
207
+ import { SquareMobilePayments } from '@capawesome/capacitor-square-mobile-payments';
137
208
 
138
209
  const checkAuthorization = async () => {
139
210
  const { authorized } = await SquareMobilePayments.isAuthorized();
140
211
  console.log('Authorized:', authorized);
141
212
  };
213
+ ```
214
+
215
+ ### Pair a Square reader
216
+
217
+ Start the pairing process. The SDK searches for nearby readers and pairs with the first one found:
218
+
219
+ ```typescript
220
+ import { SquareMobilePayments } from '@capawesome/capacitor-square-mobile-payments';
142
221
 
143
222
  const pairReader = async () => {
144
223
  await SquareMobilePayments.startPairing();
145
224
  };
225
+ ```
226
+
227
+ ### List the paired readers
228
+
229
+ Get all paired readers with their serial number, model, and status:
230
+
231
+ ```typescript
232
+ import { SquareMobilePayments } from '@capawesome/capacitor-square-mobile-payments';
146
233
 
147
234
  const getReaders = async () => {
148
235
  const { readers } = await SquareMobilePayments.getReaders();
@@ -150,6 +237,14 @@ const getReaders = async () => {
150
237
  console.log('Reader:', reader.serialNumber, reader.model, reader.status);
151
238
  }
152
239
  };
240
+ ```
241
+
242
+ ### Start a payment
243
+
244
+ Present the payment UI and process a payment with the specified parameters. Only one payment can be active at a time:
245
+
246
+ ```typescript
247
+ import { SquareMobilePayments } from '@capawesome/capacitor-square-mobile-payments';
153
248
 
154
249
  const processPayment = async () => {
155
250
  await SquareMobilePayments.startPayment({
@@ -166,6 +261,14 @@ const processPayment = async () => {
166
261
  },
167
262
  });
168
263
  };
264
+ ```
265
+
266
+ ### Listen for payment events
267
+
268
+ Listen for successful, failed, and cancelled payments to react to the result of a payment flow:
269
+
270
+ ```typescript
271
+ import { SquareMobilePayments } from '@capawesome/capacitor-square-mobile-payments';
169
272
 
170
273
  const listenToPaymentEvents = () => {
171
274
  SquareMobilePayments.addListener('paymentDidFinish', (event) => {
@@ -182,6 +285,14 @@ const listenToPaymentEvents = () => {
182
285
  console.log('Payment cancelled');
183
286
  });
184
287
  };
288
+ ```
289
+
290
+ ### Listen for reader events
291
+
292
+ Listen for reader status changes and changes to the available card input methods:
293
+
294
+ ```typescript
295
+ import { SquareMobilePayments } from '@capawesome/capacitor-square-mobile-payments';
185
296
 
186
297
  const listenToReaderEvents = () => {
187
298
  SquareMobilePayments.addListener('readerWasAdded', (event) => {
@@ -196,6 +307,14 @@ const listenToReaderEvents = () => {
196
307
  console.log('Available methods:', event.cardInputMethods);
197
308
  });
198
309
  };
310
+ ```
311
+
312
+ ### Get the available card input methods
313
+
314
+ Read the card entry methods that are currently available based on the connected readers (e.g. tap, dip, swipe, or keyed entry):
315
+
316
+ ```typescript
317
+ import { SquareMobilePayments } from '@capawesome/capacitor-square-mobile-payments';
199
318
 
200
319
  const getAvailableMethods = async () => {
201
320
  const { cardInputMethods } = await SquareMobilePayments.getAvailableCardInputMethods();
@@ -1376,6 +1495,46 @@ Callback to receive payment cancellation notifications.
1376
1495
 
1377
1496
  </docgen-api>
1378
1497
 
1498
+ ## FAQ
1499
+
1500
+ ### Which platforms are supported by this plugin?
1501
+
1502
+ The plugin supports Android and iOS. There is no Web implementation, since the Square Mobile Payments SDK requires native hardware access to card readers.
1503
+
1504
+ ### How can I test payments without a physical card reader?
1505
+
1506
+ Use the `showMockReader()` method to display a mock reader interface for testing payment flows without physical hardware. It is only intended for development and testing purposes and is therefore only available in debug builds. You can hide it again with `hideMockReader()`.
1507
+
1508
+ ### What permissions does the plugin require?
1509
+
1510
+ The plugin requires location access to confirm that payments are occurring in a supported Square location, Bluetooth access to connect to Square card readers, and, on iOS, microphone access to receive data from magstripe readers. You can check and request the required permissions with `checkPermissions()` and `requestPermissions()`, and the corresponding privacy descriptions must be added to your `Info.plist` on iOS (see [Installation](#installation)).
1511
+
1512
+ ### Does the plugin support Tap to Pay on iPhone?
1513
+
1514
+ Yes, on iOS you can link a Square seller account with an Apple ID using `linkAppleAccount()`, which presents an Apple sheet with the Tap to Pay on iPhone terms and conditions. Use `isDeviceCapable()` to check whether the device supports Tap to Pay on iPhone, and `relinkAppleAccount()` to switch to a different Apple ID.
1515
+
1516
+ ### Why do the plugin methods fail before I call any of them?
1517
+
1518
+ The Square Mobile Payments SDK must be initialized natively with your Square Application ID before the plugin can be used: in a custom `Application` class on Android and in the `AppDelegate` on iOS (see [Installation](#installation)). Additionally, `initialize(...)` must be called before any other plugin method, followed by `authorize(...)` with a Square access token.
1519
+
1520
+ ### Can I process payments without an internet connection?
1521
+
1522
+ Yes, the plugin supports processing payments online or offline with automatic sync. Note that for offline payments, the payment `id` may be `null` until the payment has been synced.
1523
+
1524
+ ### Can I use this plugin with Ionic, React, Vue or Angular?
1525
+
1526
+ Yes, the plugin is framework-agnostic. It works in any Capacitor app regardless of the web framework, including Ionic with Angular, React, or Vue, as well as plain JavaScript projects.
1527
+
1528
+ ## Related Plugins
1529
+
1530
+ - [Purchases](https://capawesome.io/docs/sdks/capacitor/purchases/): Support in-app purchases in your Capacitor app.
1531
+ - [Superwall](https://capawesome.io/docs/sdks/capacitor/superwall/): Present remotely-configured paywalls to drive subscriptions.
1532
+ - [Wallet](https://capawesome.io/docs/sdks/capacitor/wallet/): Add passes to Apple Wallet and Google Wallet.
1533
+
1534
+ ## Newsletter
1535
+
1536
+ Stay up to date with the latest news and updates about the Capawesome, Capacitor, and Ionic ecosystem by subscribing to our [Capawesome Newsletter](https://cloud.capawesome.io/newsletter/).
1537
+
1379
1538
  ## Changelog
1380
1539
 
1381
1540
  See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/square-mobile-payments/CHANGELOG.md).
@@ -1,6 +1,7 @@
1
1
  package io.capawesome.capacitorjs.plugins.squaremobilepayments;
2
2
 
3
- import android.app.Application;
3
+ import android.os.Handler;
4
+ import android.os.Looper;
4
5
  import androidx.annotation.NonNull;
5
6
  import androidx.annotation.Nullable;
6
7
  import com.getcapacitor.JSObject;
@@ -43,9 +44,15 @@ public class SquareMobilePayments {
43
44
  @NonNull
44
45
  private final SquareMobilePaymentsPlugin plugin;
45
46
 
47
+ @NonNull
48
+ private final Handler mainHandler = new Handler(Looper.getMainLooper());
49
+
46
50
  private boolean isInitialized = false;
47
51
  private boolean isAuthorized = false;
48
52
 
53
+ @Nullable
54
+ private String locationId;
55
+
49
56
  @Nullable
50
57
  private Object pairingHandle;
51
58
 
@@ -58,18 +65,30 @@ public class SquareMobilePayments {
58
65
  @Nullable
59
66
  private CallbackReference availableCardInputMethodsCallbackReference;
60
67
 
68
+ @Nullable
69
+ private CallbackReference authorizationCallbackReference;
70
+
61
71
  public SquareMobilePayments(@NonNull SquareMobilePaymentsPlugin plugin) {
62
72
  this.plugin = plugin;
63
73
  }
64
74
 
65
- private String locationId;
66
-
67
75
  public void initialize(@NonNull InitializeOptions options, @NonNull EmptyCallback callback) {
68
76
  try {
69
77
  this.locationId = options.getLocationId();
70
- Application application = (Application) plugin.getContext().getApplicationContext();
71
78
 
72
- MobilePaymentsSdk.initialize(locationId, application);
79
+ // SDK must already be initialized in Application.onCreate() with the Square Application ID.
80
+ // Verify the SDK is ready by attempting to access the settings manager.
81
+ try {
82
+ MobilePaymentsSdk.settingsManager();
83
+ } catch (Exception e) {
84
+ callback.error(
85
+ new Exception(
86
+ "Square SDK not initialized. Make sure to call MobilePaymentsSdk.initialize() in your Application.onCreate().",
87
+ e
88
+ )
89
+ );
90
+ return;
91
+ }
73
92
 
74
93
  isInitialized = true;
75
94
  callback.success();
@@ -86,13 +105,33 @@ public class SquareMobilePayments {
86
105
 
87
106
  String accessToken = options.getAccessToken();
88
107
 
89
- AuthorizationManager authManager = MobilePaymentsSdk.authorizationManager();
90
- authManager.authorize(accessToken, locationId, result -> {
91
- if (result.isSuccess()) {
92
- isAuthorized = true;
93
- callback.success();
94
- } else {
95
- callback.error(new Exception(result.errorMessage()));
108
+ mainHandler.post(() -> {
109
+ try {
110
+ AuthorizationManager authManager = MobilePaymentsSdk.authorizationManager();
111
+
112
+ authorizationCallbackReference = authManager.authorize(accessToken, locationId, result -> {
113
+ if (result.isSuccess()) {
114
+ isAuthorized = true;
115
+ callback.success();
116
+ } else {
117
+ isAuthorized = false;
118
+ String errorMsg = result.errorMessage();
119
+ if (errorMsg == null || errorMsg.isEmpty()) {
120
+ errorMsg = "Authorization failed";
121
+ }
122
+ if (result.errorCode() != null) {
123
+ errorMsg = errorMsg + " (Error code: " + result.errorCode().toString() + ")";
124
+ }
125
+ callback.error(new Exception(errorMsg));
126
+ }
127
+ if (authorizationCallbackReference != null) {
128
+ authorizationCallbackReference.clear();
129
+ authorizationCallbackReference = null;
130
+ }
131
+ });
132
+ } catch (Exception exception) {
133
+ isAuthorized = false;
134
+ callback.error(exception);
96
135
  }
97
136
  });
98
137
  } catch (Exception exception) {
@@ -107,9 +146,11 @@ public class SquareMobilePayments {
107
146
  }
108
147
 
109
148
  AuthorizationManager authManager = MobilePaymentsSdk.authorizationManager();
110
- boolean authorized = authManager.getAuthorizationState().isAuthorized();
149
+ boolean sdkAuthorized = authManager.getAuthorizationState().isAuthorized();
111
150
 
112
- IsAuthorizedResult result = new IsAuthorizedResult(authorized);
151
+ isAuthorized = sdkAuthorized;
152
+
153
+ IsAuthorizedResult result = new IsAuthorizedResult(sdkAuthorized);
113
154
  callback.success(result);
114
155
  } catch (Exception exception) {
115
156
  callback.error(exception);
@@ -122,10 +163,16 @@ public class SquareMobilePayments {
122
163
  throw CustomExceptions.NOT_INITIALIZED;
123
164
  }
124
165
 
125
- AuthorizationManager authManager = MobilePaymentsSdk.authorizationManager();
126
- authManager.deauthorize();
127
- isAuthorized = false;
128
- callback.success();
166
+ mainHandler.post(() -> {
167
+ try {
168
+ AuthorizationManager authManager = MobilePaymentsSdk.authorizationManager();
169
+ authManager.deauthorize();
170
+ isAuthorized = false;
171
+ callback.success();
172
+ } catch (Exception exception) {
173
+ callback.error(exception);
174
+ }
175
+ });
129
176
  } catch (Exception exception) {
130
177
  callback.error(exception);
131
178
  }
@@ -140,14 +187,20 @@ public class SquareMobilePayments {
140
187
  throw CustomExceptions.NOT_AUTHORIZED;
141
188
  }
142
189
 
143
- SettingsManager settingsManager = MobilePaymentsSdk.settingsManager();
144
- settingsManager.showSettings(result -> {
145
- if (result.isSuccess()) {
146
- callback.success();
147
- } else {
148
- callback.error(new Exception(result.errorMessage()));
190
+ mainHandler.post(() -> {
191
+ try {
192
+ SettingsManager settingsManager = MobilePaymentsSdk.settingsManager();
193
+ settingsManager.showSettings(result -> {
194
+ if (result.isSuccess()) {
195
+ callback.success();
196
+ } else {
197
+ callback.error(new Exception(result.errorMessage()));
198
+ }
199
+ return null;
200
+ });
201
+ } catch (Exception exception) {
202
+ callback.error(exception);
149
203
  }
150
- return null;
151
204
  });
152
205
  } catch (Exception exception) {
153
206
  callback.error(exception);
@@ -182,31 +235,34 @@ public class SquareMobilePayments {
182
235
  throw CustomExceptions.NOT_AUTHORIZED;
183
236
  }
184
237
 
185
- ReaderManager readerManager = MobilePaymentsSdk.readerManager();
186
-
187
- if (readerManager.isPairingInProgress()) {
188
- throw CustomExceptions.PAIRING_ALREADY_IN_PROGRESS;
189
- }
190
-
191
- // Notify that pairing has begun
192
- plugin.notifyReaderPairingDidBeginListeners();
238
+ mainHandler.post(() -> {
239
+ try {
240
+ ReaderManager readerManager = MobilePaymentsSdk.readerManager();
241
+
242
+ if (readerManager.isPairingInProgress()) {
243
+ throw CustomExceptions.PAIRING_ALREADY_IN_PROGRESS;
244
+ }
245
+
246
+ plugin.notifyReaderPairingDidBeginListeners();
247
+
248
+ pairingHandle = readerManager.pairReader(result -> {
249
+ if (result.isSuccess()) {
250
+ plugin.notifyReaderPairingDidSucceedListeners();
251
+ } else {
252
+ ReaderPairingDidFailEvent event = new ReaderPairingDidFailEvent(
253
+ result.errorCode() != null ? result.errorCode().toString() : null,
254
+ result.errorMessage()
255
+ );
256
+ plugin.notifyReaderPairingDidFailListeners(event);
257
+ }
258
+ pairingHandle = null;
259
+ });
193
260
 
194
- pairingHandle = readerManager.pairReader(result -> {
195
- if (result.isSuccess()) {
196
- // Notify that pairing succeeded
197
- plugin.notifyReaderPairingDidSucceedListeners();
198
- } else {
199
- // Notify that pairing failed
200
- ReaderPairingDidFailEvent event = new ReaderPairingDidFailEvent(
201
- result.errorCode() != null ? result.errorCode().toString() : null,
202
- result.errorMessage()
203
- );
204
- plugin.notifyReaderPairingDidFailListeners(event);
261
+ callback.success();
262
+ } catch (Exception exception) {
263
+ callback.error(exception);
205
264
  }
206
- pairingHandle = null;
207
265
  });
208
-
209
- callback.success();
210
266
  } catch (Exception exception) {
211
267
  callback.error(exception);
212
268
  }
@@ -335,24 +391,20 @@ public class SquareMobilePayments {
335
391
 
336
392
  PaymentManager paymentManager = MobilePaymentsSdk.paymentManager();
337
393
 
338
- // Build Money object
339
394
  Money amountMoney = new Money(params.getAmountMoney().getAmount(), CurrencyCode.valueOf(params.getAmountMoney().getCurrency()));
340
395
 
341
- // Determine processing mode
342
396
  ProcessingMode processingMode = ProcessingMode.AUTO_DETECT;
343
397
  if (params.getProcessingMode() != null) {
344
398
  processingMode = ProcessingMode.valueOf(params.getProcessingMode());
345
399
  }
346
400
 
347
- // Determine autocomplete
348
401
  boolean autocomplete = params.getAutocomplete() != null ? params.getAutocomplete() : true;
349
402
 
350
- // Build PaymentParameters
351
403
  PaymentParameters.Builder paramsBuilder = new PaymentParameters.Builder(
352
404
  amountMoney,
353
405
  params.getPaymentAttemptId(),
354
406
  processingMode,
355
- false // allowCardSurcharge - not supported yet
407
+ false
356
408
  );
357
409
 
358
410
  paramsBuilder.autocomplete(autocomplete);
@@ -393,7 +445,6 @@ public class SquareMobilePayments {
393
445
 
394
446
  PaymentParameters sdkPaymentParams = paramsBuilder.build();
395
447
 
396
- // Build PromptParameters
397
448
  PromptMode promptMode = PromptMode.DEFAULT;
398
449
  if (promptParams.getMode() != null) {
399
450
  promptMode = PromptMode.valueOf(promptParams.getMode());
@@ -406,28 +457,32 @@ public class SquareMobilePayments {
406
457
 
407
458
  PromptParameters sdkPromptParams = new PromptParameters(promptMode, additionalMethods);
408
459
 
409
- // Start payment
410
- paymentHandle = paymentManager.startPaymentActivity(sdkPaymentParams, sdkPromptParams, result -> {
411
- if (result.isSuccess()) {
412
- Payment sdkPayment = result.value();
413
- io.capawesome.capacitorjs.plugins.squaremobilepayments.classes.results.Payment payment = convertSdkPaymentToPayment(
414
- sdkPayment
415
- );
460
+ mainHandler.post(() -> {
461
+ try {
462
+ paymentHandle = paymentManager.startPaymentActivity(sdkPaymentParams, sdkPromptParams, result -> {
463
+ if (result.isSuccess()) {
464
+ Payment sdkPayment = result.value();
465
+ io.capawesome.capacitorjs.plugins.squaremobilepayments.classes.results.Payment payment =
466
+ convertSdkPaymentToPayment(sdkPayment);
467
+
468
+ PaymentDidFinishEvent event = new PaymentDidFinishEvent(payment);
469
+ plugin.notifyPaymentDidFinishListeners(event);
470
+ } else {
471
+ PaymentDidFailEvent event = new PaymentDidFailEvent(
472
+ null,
473
+ result.errorCode() != null ? result.errorCode().toString() : null,
474
+ result.errorMessage()
475
+ );
476
+ plugin.notifyPaymentDidFailListeners(event);
477
+ }
478
+ paymentHandle = null;
479
+ });
416
480
 
417
- PaymentDidFinishEvent event = new PaymentDidFinishEvent(payment);
418
- plugin.notifyPaymentDidFinishListeners(event);
419
- } else {
420
- PaymentDidFailEvent event = new PaymentDidFailEvent(
421
- null,
422
- result.errorCode() != null ? result.errorCode().toString() : null,
423
- result.errorMessage()
424
- );
425
- plugin.notifyPaymentDidFailListeners(event);
481
+ callback.success();
482
+ } catch (Exception exception) {
483
+ callback.error(exception);
426
484
  }
427
- paymentHandle = null;
428
485
  });
429
-
430
- callback.success();
431
486
  } catch (Exception exception) {
432
487
  callback.error(exception);
433
488
  }
@@ -439,17 +494,23 @@ public class SquareMobilePayments {
439
494
  throw CustomExceptions.NOT_INITIALIZED;
440
495
  }
441
496
 
442
- if (paymentHandle != null) {
443
- paymentHandle.cancel();
444
- paymentHandle = null;
497
+ mainHandler.post(() -> {
498
+ try {
499
+ if (paymentHandle != null) {
500
+ paymentHandle.cancel();
501
+ paymentHandle = null;
445
502
 
446
- PaymentDidCancelEvent event = new PaymentDidCancelEvent(null);
447
- plugin.notifyPaymentDidCancelListeners(event);
448
- } else {
449
- throw CustomExceptions.NO_PAYMENT_IN_PROGRESS;
450
- }
503
+ PaymentDidCancelEvent event = new PaymentDidCancelEvent(null);
504
+ plugin.notifyPaymentDidCancelListeners(event);
505
+ } else {
506
+ throw CustomExceptions.NO_PAYMENT_IN_PROGRESS;
507
+ }
451
508
 
452
- callback.success();
509
+ callback.success();
510
+ } catch (Exception exception) {
511
+ callback.error(exception);
512
+ }
513
+ });
453
514
  } catch (Exception exception) {
454
515
  callback.error(exception);
455
516
  }
@@ -483,15 +544,21 @@ public class SquareMobilePayments {
483
544
  throw CustomExceptions.NOT_AUTHORIZED;
484
545
  }
485
546
 
486
- // Show MockReader UI - only available in Debug builds with mockreader-ui dependency
487
- // Use reflection to avoid compile-time dependency
488
- Class<?> mockReaderUIClass = Class.forName("com.squareup.sdk.mockreader.ui.MockReaderUI");
489
- mockReaderUIClass.getMethod("show").invoke(null);
490
- callback.success();
491
- } catch (ClassNotFoundException e) {
492
- callback.error(
493
- new Exception("MockReaderUI is only available in Debug builds. Please ensure you're using a Debug build configuration.")
494
- );
547
+ mainHandler.post(() -> {
548
+ try {
549
+ Class<?> mockReaderUIClass = Class.forName("com.squareup.sdk.mockreader.ui.MockReaderUI");
550
+ mockReaderUIClass.getMethod("show").invoke(null);
551
+ callback.success();
552
+ } catch (ClassNotFoundException e) {
553
+ callback.error(
554
+ new Exception(
555
+ "MockReaderUI is only available in Debug builds. Please ensure you're using a Debug build configuration."
556
+ )
557
+ );
558
+ } catch (Exception exception) {
559
+ callback.error(exception);
560
+ }
561
+ });
495
562
  } catch (Exception exception) {
496
563
  callback.error(exception);
497
564
  }
@@ -499,15 +566,21 @@ public class SquareMobilePayments {
499
566
 
500
567
  public void hideMockReader(@NonNull EmptyCallback callback) {
501
568
  try {
502
- // Hide MockReader UI
503
- // Use reflection to avoid compile-time dependency
504
- Class<?> mockReaderUIClass = Class.forName("com.squareup.sdk.mockreader.ui.MockReaderUI");
505
- mockReaderUIClass.getMethod("hide").invoke(null);
506
- callback.success();
507
- } catch (ClassNotFoundException e) {
508
- callback.error(
509
- new Exception("MockReaderUI is only available in Debug builds. Please ensure you're using a Debug build configuration.")
510
- );
569
+ mainHandler.post(() -> {
570
+ try {
571
+ Class<?> mockReaderUIClass = Class.forName("com.squareup.sdk.mockreader.ui.MockReaderUI");
572
+ mockReaderUIClass.getMethod("hide").invoke(null);
573
+ callback.success();
574
+ } catch (ClassNotFoundException e) {
575
+ callback.error(
576
+ new Exception(
577
+ "MockReaderUI is only available in Debug builds. Please ensure you're using a Debug build configuration."
578
+ )
579
+ );
580
+ } catch (Exception exception) {
581
+ callback.error(exception);
582
+ }
583
+ });
511
584
  } catch (Exception exception) {
512
585
  callback.error(exception);
513
586
  }
@@ -528,7 +601,6 @@ public class SquareMobilePayments {
528
601
  sdkReader
529
602
  );
530
603
 
531
- // Notify based on change type
532
604
  if (change == ReaderChangedEvent.Change.ADDED) {
533
605
  ReaderWasAddedEvent addedEvent = new ReaderWasAddedEvent(reader);
534
606
  plugin.notifyReaderWasAddedListeners(addedEvent);
@@ -537,7 +609,6 @@ public class SquareMobilePayments {
537
609
  plugin.notifyReaderWasRemovedListeners(removedEvent);
538
610
  }
539
611
 
540
- // Also notify general reader changed event
541
612
  ReaderDidChangeEvent didChangeEvent = new ReaderDidChangeEvent(reader, convertReaderChangeToString(change));
542
613
  plugin.notifyReaderDidChangeListeners(didChangeEvent);
543
614
  });
@@ -572,8 +643,6 @@ public class SquareMobilePayments {
572
643
  }
573
644
  }
574
645
 
575
- // Helper methods for converting SDK types to plugin types
576
-
577
646
  @Nullable
578
647
  private com.squareup.sdk.mobilepayments.cardreader.ReaderInfo findReaderBySerialNumber(@NonNull String serialNumber) {
579
648
  ReaderManager readerManager = MobilePaymentsSdk.readerManager();
@@ -596,8 +665,14 @@ public class SquareMobilePayments {
596
665
  String model = convertReaderModelToString(sdkReader.getModel());
597
666
  String status = convertReaderStatusToString(sdkReader.getStatus());
598
667
  String firmwareVersion = sdkReader.getFirmwareVersion();
599
- Integer batteryLevel = sdkReader.getBatteryStatus().getPercent();
600
- Boolean isCharging = sdkReader.getBatteryStatus().isCharging();
668
+
669
+ Integer batteryLevel = null;
670
+ Boolean isCharging = null;
671
+ com.squareup.sdk.mobilepayments.cardreader.ReaderInfo.BatteryStatus batteryStatus = sdkReader.getBatteryStatus();
672
+ if (batteryStatus != null) {
673
+ batteryLevel = batteryStatus.getPercent();
674
+ isCharging = batteryStatus.isCharging();
675
+ }
601
676
 
602
677
  List<String> supportedCardInputMethods = sdkReader
603
678
  .getSupportedCardEntryMethods()
@@ -638,7 +713,6 @@ public class SquareMobilePayments {
638
713
  type = "ONLINE";
639
714
  status = convertPaymentStatusToString(onlinePayment.getStatus());
640
715
 
641
- // Extract card details
642
716
  com.squareup.sdk.mobilepayments.payment.CardPaymentDetails sdkCardDetails = onlinePayment.getCardDetails();
643
717
  if (sdkCardDetails != null) {
644
718
  cardDetails = convertSdkCardDetailsToCardPaymentDetails(sdkCardDetails);
@@ -649,7 +723,6 @@ public class SquareMobilePayments {
649
723
  type = "OFFLINE";
650
724
  status = "PENDING";
651
725
 
652
- // Extract card details
653
726
  com.squareup.sdk.mobilepayments.payment.CardPaymentDetails sdkCardDetails = offlinePayment.getCardDetails();
654
727
  if (sdkCardDetails != null) {
655
728
  cardDetails = convertSdkCardDetailsToCardPaymentDetails(sdkCardDetails);
@@ -664,13 +737,15 @@ public class SquareMobilePayments {
664
737
  sdkPayment.getTotalMoney().getCurrencyCode().toString()
665
738
  );
666
739
 
667
- MoneyResult tipMoney = sdkPayment.getTipMoney() != null
668
- ? new MoneyResult((int) sdkPayment.getTipMoney().getAmount(), sdkPayment.getTipMoney().getCurrencyCode().toString())
669
- : null;
740
+ MoneyResult tipMoney =
741
+ sdkPayment.getTipMoney() != null
742
+ ? new MoneyResult((int) sdkPayment.getTipMoney().getAmount(), sdkPayment.getTipMoney().getCurrencyCode().toString())
743
+ : null;
670
744
 
671
- MoneyResult applicationFee = sdkPayment.getAppFeeMoney() != null
672
- ? new MoneyResult((int) sdkPayment.getAppFeeMoney().getAmount(), sdkPayment.getAppFeeMoney().getCurrencyCode().toString())
673
- : null;
745
+ MoneyResult applicationFee =
746
+ sdkPayment.getAppFeeMoney() != null
747
+ ? new MoneyResult((int) sdkPayment.getAppFeeMoney().getAmount(), sdkPayment.getAppFeeMoney().getCurrencyCode().toString())
748
+ : null;
674
749
 
675
750
  return new io.capawesome.capacitorjs.plugins.squaremobilepayments.classes.results.Payment(
676
751
  id,
@@ -770,7 +845,6 @@ public class SquareMobilePayments {
770
845
  } else if (change == ReaderChangedEvent.Change.REMOVED) {
771
846
  return "REMOVED";
772
847
  }
773
- // Default for any other status changes
774
848
  return "STATUS_DID_CHANGE";
775
849
  }
776
850
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capawesome/capacitor-square-mobile-payments",
3
- "version": "0.1.4",
4
- "description": "Unofficial Capacitor plugin for Square Mobile Payments SDK.",
3
+ "version": "0.1.6",
4
+ "description": "Unofficial Capacitor plugin for Square Mobile Payments SDK to accept in-person payments on Android and iOS.",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",
7
7
  "types": "dist/esm/index.d.ts",
@@ -36,7 +36,16 @@
36
36
  "keywords": [
37
37
  "capacitor",
38
38
  "plugin",
39
- "native"
39
+ "native",
40
+ "capacitor-plugin",
41
+ "square",
42
+ "mobile payments",
43
+ "in-person payments",
44
+ "card reader",
45
+ "point of sale",
46
+ "pos",
47
+ "tap to pay",
48
+ "payment processing"
40
49
  ],
41
50
  "scripts": {
42
51
  "verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
@@ -58,24 +67,21 @@
58
67
  },
59
68
  "devDependencies": {
60
69
  "@capacitor/android": "8.0.0",
61
- "@capacitor/cli": "8.0.0",
70
+ "@capacitor/cli": "8.4.2",
62
71
  "@capacitor/core": "8.0.0",
63
72
  "@capacitor/docgen": "0.3.1",
64
73
  "@capacitor/ios": "8.0.0",
65
74
  "@ionic/eslint-config": "0.4.0",
66
- "@ionic/swiftlint-config": "2.0.0",
67
75
  "eslint": "8.57.0",
68
- "prettier": "3.4.2",
69
- "prettier-plugin-java": "2.6.7",
76
+ "prettier-plugin-java": "2.9.7",
70
77
  "rimraf": "6.1.2",
71
- "rollup": "4.53.3",
78
+ "rollup": "4.62.3",
72
79
  "swiftlint": "2.0.0",
73
80
  "typescript": "5.9.3"
74
81
  },
75
82
  "peerDependencies": {
76
83
  "@capacitor/core": ">=8.0.0"
77
84
  },
78
- "swiftlint": "@ionic/swiftlint-config",
79
85
  "eslintConfig": {
80
86
  "extends": "@ionic/eslint-config/recommended"
81
87
  },