@capgo/capacitor-pay 8.1.0 → 8.1.2
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/README.md +355 -69
- package/android/build.gradle +1 -1
- package/android/src/main/java/app/capgo/pay/PayPlugin.java +217 -57
- package/dist/docs.json +1288 -144
- package/dist/esm/definitions.d.ts +555 -26
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.js +1 -0
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +1 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +1 -0
- package/dist/plugin.js.map +1 -1
- package/docs/google-pay-setup.md +33 -7
- package/ios/Sources/PayPlugin/PayPlugin.swift +1 -1
- package/package.json +3 -3
|
@@ -2,6 +2,11 @@ package app.capgo.pay;
|
|
|
2
2
|
|
|
3
3
|
import android.app.Activity;
|
|
4
4
|
import android.content.Intent;
|
|
5
|
+
import androidx.activity.ComponentActivity;
|
|
6
|
+
import androidx.activity.result.ActivityResult;
|
|
7
|
+
import androidx.activity.result.ActivityResultLauncher;
|
|
8
|
+
import androidx.activity.result.IntentSenderRequest;
|
|
9
|
+
import androidx.activity.result.contract.ActivityResultContracts;
|
|
5
10
|
import androidx.annotation.Nullable;
|
|
6
11
|
import com.getcapacitor.JSObject;
|
|
7
12
|
import com.getcapacitor.Logger;
|
|
@@ -10,6 +15,8 @@ import com.getcapacitor.PluginCall;
|
|
|
10
15
|
import com.getcapacitor.PluginMethod;
|
|
11
16
|
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
12
17
|
import com.google.android.gms.common.api.ApiException;
|
|
18
|
+
import com.google.android.gms.common.api.CommonStatusCodes;
|
|
19
|
+
import com.google.android.gms.common.api.ResolvableApiException;
|
|
13
20
|
import com.google.android.gms.common.api.Status;
|
|
14
21
|
import com.google.android.gms.tasks.Task;
|
|
15
22
|
import com.google.android.gms.wallet.AutoResolveHelper;
|
|
@@ -26,11 +33,23 @@ import org.json.JSONObject;
|
|
|
26
33
|
@CapacitorPlugin(name = "Pay")
|
|
27
34
|
public class PayPlugin extends Plugin {
|
|
28
35
|
|
|
29
|
-
private final String pluginVersion = "8.1.
|
|
36
|
+
private final String pluginVersion = "8.1.2";
|
|
30
37
|
|
|
31
|
-
private
|
|
38
|
+
private boolean paymentInProgress = false;
|
|
39
|
+
private boolean resolutionInProgress = false;
|
|
40
|
+
private ActivityResultLauncher<IntentSenderRequest> googlePayLauncher;
|
|
41
|
+
private PaymentsClient paymentsClient;
|
|
42
|
+
private int lastEnv = -1;
|
|
43
|
+
private PluginCall pendingGooglePayCall;
|
|
32
44
|
|
|
33
|
-
|
|
45
|
+
@Override
|
|
46
|
+
public void load() {
|
|
47
|
+
if (getActivity() == null) {
|
|
48
|
+
Logger.warn("PayPlugin", "Google Pay launcher unavailable during load(): no active activity.");
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
this.ensureLauncher();
|
|
52
|
+
}
|
|
34
53
|
|
|
35
54
|
@PluginMethod
|
|
36
55
|
public void isPayAvailable(PluginCall call) {
|
|
@@ -80,11 +99,16 @@ public class PayPlugin extends Plugin {
|
|
|
80
99
|
|
|
81
100
|
@PluginMethod
|
|
82
101
|
public void requestPayment(PluginCall call) {
|
|
83
|
-
if (
|
|
102
|
+
if (this.paymentInProgress) {
|
|
84
103
|
call.reject("Another Google Pay request is already in progress.");
|
|
85
104
|
return;
|
|
86
105
|
}
|
|
87
106
|
|
|
107
|
+
if (!ensureLauncher()) {
|
|
108
|
+
call.reject("No active activity to start Google Pay.");
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
88
112
|
JSObject googleOptions = call.getObject("google");
|
|
89
113
|
if (googleOptions == null) {
|
|
90
114
|
call.reject("Google Pay configuration is required on Android.");
|
|
@@ -111,77 +135,209 @@ public class PayPlugin extends Plugin {
|
|
|
111
135
|
|
|
112
136
|
try {
|
|
113
137
|
PaymentDataRequest request = PaymentDataRequest.fromJson(paymentRequestJson.toString());
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
pendingPaymentCall = call;
|
|
120
|
-
Task<PaymentData> task = client.loadPaymentData(request);
|
|
121
|
-
Activity activity = getActivity();
|
|
122
|
-
if (activity == null) {
|
|
123
|
-
pendingPaymentCall = null;
|
|
124
|
-
call.reject("No active activity to present Google Pay.");
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
AutoResolveHelper.resolveTask(task, activity, LOAD_PAYMENT_DATA_REQUEST_CODE);
|
|
138
|
+
this.paymentInProgress = true;
|
|
139
|
+
this.pendingGooglePayCall = call;
|
|
140
|
+
client.loadPaymentData(request).addOnSuccessListener(this::emitAuthorized).addOnFailureListener(this::emitError);
|
|
128
141
|
} catch (Exception ex) {
|
|
129
|
-
|
|
142
|
+
this.paymentInProgress = false;
|
|
143
|
+
this.pendingGooglePayCall = null;
|
|
130
144
|
call.reject("Failed to launch Google Pay.", ex);
|
|
131
145
|
}
|
|
132
146
|
}
|
|
133
147
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
148
|
+
private boolean ensureLauncher() {
|
|
149
|
+
if (googlePayLauncher != null) return true;
|
|
150
|
+
|
|
151
|
+
ComponentActivity activity = getActivity();
|
|
152
|
+
if (activity == null) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
googlePayLauncher = activity.registerForActivityResult(
|
|
158
|
+
new ActivityResultContracts.StartIntentSenderForResult(),
|
|
159
|
+
this::handleGooglePayResult
|
|
160
|
+
);
|
|
161
|
+
return true;
|
|
162
|
+
} catch (IllegalStateException ex) {
|
|
163
|
+
Logger.warn("PayPlugin", "Failed to register Google Pay launcher: " + ex.getLocalizedMessage());
|
|
164
|
+
googlePayLauncher = null;
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private void handleGooglePayResult(ActivityResult activityResult) {
|
|
170
|
+
resolutionInProgress = false;
|
|
171
|
+
paymentInProgress = false; // important: user interaction ended
|
|
172
|
+
|
|
173
|
+
int resultCode = activityResult.getResultCode();
|
|
174
|
+
Intent data = activityResult.getData();
|
|
137
175
|
|
|
138
|
-
|
|
176
|
+
Logger.debug("PayPlugin", "GooglePay resultCode=" + resultCode);
|
|
177
|
+
|
|
178
|
+
if (resultCode == Activity.RESULT_OK) {
|
|
179
|
+
emitAuthorizedFromIntent(data);
|
|
139
180
|
return;
|
|
140
181
|
}
|
|
141
182
|
|
|
142
|
-
if (
|
|
183
|
+
if (resultCode == Activity.RESULT_CANCELED) {
|
|
184
|
+
this.emitCancel();
|
|
143
185
|
return;
|
|
144
186
|
}
|
|
145
187
|
|
|
146
|
-
|
|
147
|
-
|
|
188
|
+
if (resultCode == AutoResolveHelper.RESULT_ERROR) {
|
|
189
|
+
Status status = AutoResolveHelper.getStatusFromIntent(data);
|
|
190
|
+
String message = (status != null && status.getStatusMessage() != null)
|
|
191
|
+
? status.getStatusMessage()
|
|
192
|
+
: "Google Pay returned an error.";
|
|
193
|
+
this.emitError(new Exception(message), "GOOGLE_PAY_API_ERROR", status);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
148
196
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
197
|
+
String message = "Google Pay returned unexpected result code: " + resultCode;
|
|
198
|
+
Exception ex = new Exception(message);
|
|
199
|
+
this.emitError(ex, "UNEXPECTED_ACTIVITY_RESULT");
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private void emitAuthorizedFromIntent(Intent data) {
|
|
203
|
+
if (data == null) {
|
|
204
|
+
Exception ex = new Exception("Google Pay returned no data.");
|
|
205
|
+
this.emitError(ex, "NO_RESULT_DATA");
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
PaymentData paymentData = PaymentData.getFromIntent(data);
|
|
210
|
+
if (paymentData == null) {
|
|
211
|
+
Exception ex = new Exception("Google Pay returned empty payment data.");
|
|
212
|
+
this.emitError(ex, "EMPTY_PAYMENT_DATA");
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
emitAuthorized(paymentData);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
private void emitAuthorized(PaymentData paymentData) {
|
|
220
|
+
try {
|
|
221
|
+
String json = paymentData.toJson();
|
|
222
|
+
JSONObject paymentDataJson = new JSONObject(json);
|
|
223
|
+
|
|
224
|
+
JSObject googleResult = new JSObject();
|
|
225
|
+
googleResult.put("paymentData", JSObject.fromJSONObject(paymentDataJson));
|
|
226
|
+
|
|
227
|
+
JSObject result = new JSObject();
|
|
228
|
+
result.put("platform", "android");
|
|
229
|
+
result.put("google", googleResult);
|
|
154
230
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
231
|
+
Logger.debug("PayPlugin", "Payment authorized");
|
|
232
|
+
resolvePendingGooglePayCall(result);
|
|
233
|
+
|
|
234
|
+
this.paymentInProgress = false;
|
|
235
|
+
} catch (JSONException ex) {
|
|
236
|
+
this.emitError(ex, "PARSE_ERROR");
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private void emitError(Exception ex) {
|
|
241
|
+
this.emitError(ex, null, null);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
private void emitError(Exception ex, String reason) {
|
|
245
|
+
this.emitError(ex, reason, null);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private void emitError(Exception ex, String reason, @Nullable Status status) {
|
|
249
|
+
if (ex instanceof ResolvableApiException rae) {
|
|
250
|
+
if (this.resolutionInProgress) {
|
|
251
|
+
// don't relaunch; treat as error
|
|
252
|
+
this.resolutionInProgress = false;
|
|
253
|
+
} else {
|
|
254
|
+
this.resolutionInProgress = true;
|
|
255
|
+
IntentSenderRequest isr = new IntentSenderRequest.Builder(rae.getResolution()).build();
|
|
256
|
+
googlePayLauncher.launch(isr);
|
|
158
257
|
return;
|
|
159
258
|
}
|
|
259
|
+
}
|
|
160
260
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
JSONObject paymentDataJson = json != null ? new JSONObject(json) : new JSONObject();
|
|
164
|
-
JSObject googleResult = new JSObject();
|
|
165
|
-
googleResult.put("paymentData", JSObject.fromJSONObject(paymentDataJson));
|
|
261
|
+
this.paymentInProgress = false;
|
|
262
|
+
this.resolutionInProgress = false;
|
|
166
263
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
result.put("google", googleResult);
|
|
264
|
+
JSObject error = new JSObject();
|
|
265
|
+
String message = ex.getMessage() != null ? ex.getMessage() : "Google Pay failed.";
|
|
170
266
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
267
|
+
if (ex instanceof JSONException) {
|
|
268
|
+
message = "Failed to parse Google Pay result.";
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
error.put("message", message);
|
|
272
|
+
error.put("platform", "android");
|
|
273
|
+
error.put("statusCode", "ERROR");
|
|
274
|
+
error.put("reason", reason != null ? reason : inferErrorReason(ex));
|
|
275
|
+
|
|
276
|
+
Integer googleStatusCode = null;
|
|
277
|
+
String googleStatusMessage = null;
|
|
278
|
+
if (status != null) {
|
|
279
|
+
googleStatusCode = status.getStatusCode();
|
|
280
|
+
googleStatusMessage = status.getStatusMessage();
|
|
281
|
+
} else if (ex instanceof ApiException apiException) {
|
|
282
|
+
googleStatusCode = apiException.getStatusCode();
|
|
283
|
+
googleStatusMessage = apiException.getStatusMessage();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (googleStatusCode != null) {
|
|
287
|
+
error.put("googleStatusCode", googleStatusCode);
|
|
288
|
+
error.put("googleStatusCodeName", CommonStatusCodes.getStatusCodeString(googleStatusCode));
|
|
289
|
+
}
|
|
290
|
+
if (googleStatusMessage != null) {
|
|
291
|
+
error.put("googleStatusMessage", googleStatusMessage);
|
|
292
|
+
}
|
|
293
|
+
if (ex instanceof ResolvableApiException) {
|
|
294
|
+
error.put("resolvable", true);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
Logger.error("PayPlugin", message, ex);
|
|
298
|
+
rejectPendingGooglePayCall(message, reason != null ? reason : inferErrorReason(ex), ex, error);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
private void emitCancel() {
|
|
302
|
+
this.paymentInProgress = false;
|
|
303
|
+
JSObject result = new JSObject();
|
|
304
|
+
String message = "Payment canceled";
|
|
305
|
+
result.put("message", message);
|
|
306
|
+
result.put("platform", "android");
|
|
307
|
+
result.put("statusCode", "CANCELED");
|
|
308
|
+
result.put("reason", "CANCELED");
|
|
309
|
+
|
|
310
|
+
Logger.debug("PayPlugin", message);
|
|
311
|
+
rejectPendingGooglePayCall(message, "CANCELED", null, result);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private String inferErrorReason(Exception ex) {
|
|
315
|
+
if (ex instanceof JSONException) {
|
|
316
|
+
return "PARSE_ERROR";
|
|
317
|
+
}
|
|
318
|
+
if (ex instanceof ApiException) {
|
|
319
|
+
return "GOOGLE_PAY_API_ERROR";
|
|
320
|
+
}
|
|
321
|
+
return "UNKNOWN";
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
private void resolvePendingGooglePayCall(JSObject result) {
|
|
325
|
+
PluginCall call = this.pendingGooglePayCall;
|
|
326
|
+
this.pendingGooglePayCall = null;
|
|
327
|
+
if (call != null) {
|
|
328
|
+
call.resolve(result);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
private void rejectPendingGooglePayCall(String message, @Nullable String code, @Nullable Exception error, @Nullable JSObject data) {
|
|
333
|
+
PluginCall call = this.pendingGooglePayCall;
|
|
334
|
+
this.pendingGooglePayCall = null;
|
|
335
|
+
if (call != null) {
|
|
336
|
+
if (error != null) {
|
|
337
|
+
call.reject(message, code, error, data);
|
|
338
|
+
} else {
|
|
339
|
+
call.reject(message, code, data);
|
|
174
340
|
}
|
|
175
|
-
} else if (resultCode == Activity.RESULT_CANCELED) {
|
|
176
|
-
call.reject("Payment canceled.");
|
|
177
|
-
} else if (resultCode == AutoResolveHelper.RESULT_ERROR) {
|
|
178
|
-
Status status = AutoResolveHelper.getStatusFromIntent(data);
|
|
179
|
-
String message = status != null && status.getStatusMessage() != null
|
|
180
|
-
? status.getStatusMessage()
|
|
181
|
-
: "Google Pay returned an error.";
|
|
182
|
-
call.reject(message);
|
|
183
|
-
} else {
|
|
184
|
-
call.reject("Unexpected activity result: " + resultCode);
|
|
185
341
|
}
|
|
186
342
|
}
|
|
187
343
|
|
|
@@ -198,8 +354,12 @@ public class PayPlugin extends Plugin {
|
|
|
198
354
|
}
|
|
199
355
|
|
|
200
356
|
private PaymentsClient createPaymentsClient(int environment) {
|
|
201
|
-
|
|
202
|
-
|
|
357
|
+
if (paymentsClient == null || lastEnv != environment) {
|
|
358
|
+
Wallet.WalletOptions options = new Wallet.WalletOptions.Builder().setEnvironment(environment).build();
|
|
359
|
+
paymentsClient = Wallet.getPaymentsClient(getContext(), options);
|
|
360
|
+
lastEnv = environment;
|
|
361
|
+
}
|
|
362
|
+
return paymentsClient;
|
|
203
363
|
}
|
|
204
364
|
|
|
205
365
|
private int parseEnvironment(@Nullable String environment) {
|