@tagadapay/plugin-sdk 3.1.25 → 4.0.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/dist/external-tracker.js +160 -6
- package/dist/external-tracker.min.js +2 -2
- package/dist/external-tracker.min.js.map +4 -4
- package/dist/react/config/payment.d.ts +2 -2
- package/dist/react/config/payment.js +5 -5
- package/dist/react/hooks/usePayment.d.ts +7 -0
- package/dist/react/hooks/usePayment.js +1 -0
- package/dist/tagada-react-sdk-minimal.min.js +2 -2
- package/dist/tagada-react-sdk-minimal.min.js.map +4 -4
- package/dist/tagada-react-sdk.js +2220 -1428
- package/dist/tagada-react-sdk.min.js +2 -2
- package/dist/tagada-react-sdk.min.js.map +4 -4
- package/dist/tagada-sdk.js +3784 -128
- package/dist/tagada-sdk.min.js +2 -2
- package/dist/tagada-sdk.min.js.map +4 -4
- package/dist/v2/core/config/environment.d.ts +3 -3
- package/dist/v2/core/config/environment.js +7 -7
- package/dist/v2/core/funnelClient.d.ts +42 -0
- package/dist/v2/core/funnelClient.js +30 -0
- package/dist/v2/core/pixelTracker.d.ts +51 -0
- package/dist/v2/core/pixelTracker.js +425 -0
- package/dist/v2/core/resources/checkout.d.ts +45 -1
- package/dist/v2/core/resources/checkout.js +13 -3
- package/dist/v2/core/resources/funnel.d.ts +1 -1
- package/dist/v2/core/resources/geo.d.ts +50 -0
- package/dist/v2/core/resources/geo.js +35 -0
- package/dist/v2/core/resources/offers.d.ts +1 -1
- package/dist/v2/core/resources/offers.js +3 -1
- package/dist/v2/core/resources/payments.d.ts +19 -1
- package/dist/v2/core/resources/payments.js +8 -0
- package/dist/v2/core/resources/promotionEvents.d.ts +5 -0
- package/dist/v2/core/resources/promotionEvents.js +2 -0
- package/dist/v2/core/resources/promotions.d.ts +6 -1
- package/dist/v2/core/resources/promotions.js +6 -1
- package/dist/v2/core/resources/shippingRates.d.ts +18 -0
- package/dist/v2/core/resources/shippingRates.js +18 -0
- package/dist/v2/core/utils/clickIdResolver.d.ts +79 -0
- package/dist/v2/core/utils/clickIdResolver.js +169 -0
- package/dist/v2/core/utils/index.d.ts +2 -0
- package/dist/v2/core/utils/index.js +4 -0
- package/dist/v2/core/utils/metaEventId.d.ts +14 -0
- package/dist/v2/core/utils/metaEventId.js +16 -0
- package/dist/v2/index.d.ts +7 -0
- package/dist/v2/index.js +10 -0
- package/dist/v2/react/components/ApplePayButton.js +50 -0
- package/dist/v2/react/components/FunnelScriptInjector.js +158 -10
- package/dist/v2/react/components/GooglePayButton.js +39 -1
- package/dist/v2/react/components/StripeExpressButton.d.ts +8 -0
- package/dist/v2/react/components/StripeExpressButton.js +76 -3
- package/dist/v2/react/hooks/payment-actions/useNgeniusThreedsAction.d.ts +15 -0
- package/dist/v2/react/hooks/payment-actions/useNgeniusThreedsAction.js +166 -0
- package/dist/v2/react/hooks/payment-actions/usePaymentActionHandler.js +12 -0
- package/dist/v2/react/hooks/payment-processing/usePaymentProcessors.js +1 -0
- package/dist/v2/react/hooks/useCheckoutQuery.js +41 -29
- package/dist/v2/react/hooks/useDiscountsQuery.js +4 -0
- package/dist/v2/react/hooks/useFunnel.d.ts +7 -0
- package/dist/v2/react/hooks/useFunnel.js +2 -1
- package/dist/v2/react/hooks/useISOData.js +25 -7
- package/dist/v2/react/hooks/usePaymentPolling.d.ts +1 -1
- package/dist/v2/react/hooks/usePixelTracking.d.ts +10 -5
- package/dist/v2/react/hooks/usePixelTracking.js +32 -374
- package/dist/v2/react/hooks/usePreviewOffer.d.ts +3 -1
- package/dist/v2/react/hooks/usePreviewOffer.js +8 -2
- package/dist/v2/react/hooks/usePromotionsQuery.js +9 -3
- package/dist/v2/react/hooks/useShippingRatesQuery.js +36 -21
- package/dist/v2/react/hooks/useStepConfig.d.ts +9 -0
- package/dist/v2/react/hooks/useStepConfig.js +5 -1
- package/dist/v2/react/index.d.ts +4 -0
- package/dist/v2/react/index.js +8 -0
- package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +12 -4
- package/dist/v2/react/providers/TagadaProvider.js +13 -0
- package/dist/v2/standalone/apple-pay-service.d.ts +12 -0
- package/dist/v2/standalone/apple-pay-service.js +12 -0
- package/dist/v2/standalone/external-tracker.d.ts +1 -1
- package/dist/v2/standalone/google-pay-service.d.ts +9 -0
- package/dist/v2/standalone/google-pay-service.js +9 -0
- package/dist/v2/standalone/index.d.ts +11 -1
- package/dist/v2/standalone/index.js +30 -0
- package/dist/v2/standalone/payment-service.d.ts +72 -6
- package/dist/v2/standalone/payment-service.js +285 -65
- package/package.json +2 -1
|
@@ -212,6 +212,7 @@ export class PaymentService {
|
|
|
212
212
|
paymentFlowId,
|
|
213
213
|
processorId: extra?.processorId,
|
|
214
214
|
paymentMethod: extra?.paymentMethod,
|
|
215
|
+
shippingRateId: extra?.shippingRateId,
|
|
215
216
|
});
|
|
216
217
|
console.log('[PaymentService] Payment response:', {
|
|
217
218
|
paymentId: response.payment?.id,
|
|
@@ -220,13 +221,40 @@ export class PaymentService {
|
|
|
220
221
|
});
|
|
221
222
|
this.callbacks.onCurrentPaymentId?.(response.payment?.id || null);
|
|
222
223
|
if (response.payment.requireAction !== 'none') {
|
|
223
|
-
|
|
224
|
-
|
|
224
|
+
// Pre-empt terminal failure surfaced through requireAction='error' (e.g.
|
|
225
|
+
// Stripe rejecting an APM that isn't enabled on the account) or status
|
|
226
|
+
// already declined/failed. Don't even invoke the action handler — for
|
|
227
|
+
// 'error' it would just fire onError, for declined+redirect it might
|
|
228
|
+
// navigate to a stale URL. Surface the failure so the button resets.
|
|
229
|
+
if (response.payment.requireAction === 'error'
|
|
230
|
+
|| response.payment.status === 'declined'
|
|
231
|
+
|| response.payment.status === 'failed') {
|
|
232
|
+
const msg = response.payment.requireActionData?.message
|
|
233
|
+
|| response.payment.error?.message
|
|
234
|
+
|| 'Payment declined';
|
|
235
|
+
this.callbacks.onError?.(msg);
|
|
236
|
+
this.callbacks.onProcessing?.(false);
|
|
237
|
+
return { success: false, error: msg, payment: response.payment, order: response.order };
|
|
238
|
+
}
|
|
239
|
+
// Run the action handler; propagate its outcome (covers radar declines,
|
|
240
|
+
// missing redirect URLs, etc. — not just the headline 'error' case).
|
|
241
|
+
const outcome = await this.handlePaymentAction(response.payment);
|
|
242
|
+
const settled = this.settleActionOutcome(outcome, response.payment, response.order);
|
|
243
|
+
if (settled)
|
|
244
|
+
return settled;
|
|
245
|
+
// 'pending' falls through to polling
|
|
225
246
|
}
|
|
226
247
|
if (response.payment.status === 'succeeded') {
|
|
227
248
|
this.callbacks.onProcessing?.(false);
|
|
228
249
|
return { success: true, payment: response.payment, order: response.order };
|
|
229
250
|
}
|
|
251
|
+
if (response.payment.status === 'declined'
|
|
252
|
+
|| response.payment.status === 'failed') {
|
|
253
|
+
const msg = response.payment.error?.message || 'Payment declined';
|
|
254
|
+
this.callbacks.onError?.(msg);
|
|
255
|
+
this.callbacks.onProcessing?.(false);
|
|
256
|
+
return { success: false, error: msg, payment: response.payment, order: response.order };
|
|
257
|
+
}
|
|
230
258
|
return new Promise((resolve) => {
|
|
231
259
|
this.startPolling(response.payment.id, {
|
|
232
260
|
onSuccess: (payment) => {
|
|
@@ -238,14 +266,42 @@ export class PaymentService {
|
|
|
238
266
|
this.callbacks.onProcessing?.(false);
|
|
239
267
|
resolve({ success: false, error });
|
|
240
268
|
},
|
|
241
|
-
onRequireAction: (payment) => {
|
|
242
|
-
|
|
269
|
+
onRequireAction: async (payment) => {
|
|
270
|
+
// Polling found a new requireAction mid-flight. Run the handler and
|
|
271
|
+
// resolve the outer promise with its outcome — otherwise the caller
|
|
272
|
+
// hangs forever waiting on a polling loop that already stopped.
|
|
273
|
+
const outcome = await this.handlePaymentAction(payment);
|
|
274
|
+
const settled = this.settleActionOutcome(outcome, payment, response.order);
|
|
275
|
+
if (settled) {
|
|
276
|
+
this.callbacks.onProcessing?.(false);
|
|
277
|
+
resolve(settled);
|
|
278
|
+
}
|
|
279
|
+
// 'pending' outcome means the action handler started its own polling;
|
|
280
|
+
// the outer promise stays open and will resolve via that path.
|
|
243
281
|
},
|
|
244
282
|
});
|
|
245
283
|
});
|
|
246
284
|
}
|
|
285
|
+
/**
|
|
286
|
+
* Translate an ActionOutcome into a PaymentResult, or null when the outcome
|
|
287
|
+
* is 'pending' (caller should keep polling instead of resolving).
|
|
288
|
+
*/
|
|
289
|
+
settleActionOutcome(outcome, payment, order) {
|
|
290
|
+
switch (outcome.kind) {
|
|
291
|
+
case 'redirected':
|
|
292
|
+
return { success: true, payment, order, redirecting: true };
|
|
293
|
+
case 'completed':
|
|
294
|
+
return { success: true, payment: outcome.payment, order };
|
|
295
|
+
case 'failed':
|
|
296
|
+
return { success: false, error: outcome.error, payment: outcome.payment ?? payment, order };
|
|
297
|
+
case 'pending':
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
247
301
|
/**
|
|
248
302
|
* After radar / completePaymentAfterAction, handle the resumed payment.
|
|
303
|
+
* Fires callbacks for side effects AND returns an outcome so the caller
|
|
304
|
+
* (handlePaymentAction) can propagate failure into PaymentResult.
|
|
249
305
|
*/
|
|
250
306
|
async handleResumedPayment(resumedPayment) {
|
|
251
307
|
if (resumedPayment.status === 'declined' || resumedPayment.status === 'failed') {
|
|
@@ -255,18 +311,17 @@ export class PaymentService {
|
|
|
255
311
|
this.callbacks.onError?.(errorMsg);
|
|
256
312
|
this.callbacks.onProcessing?.(false);
|
|
257
313
|
this.callbacks.onFailure?.(errorMsg);
|
|
258
|
-
return;
|
|
314
|
+
return { kind: 'failed', error: errorMsg, payment: resumedPayment };
|
|
259
315
|
}
|
|
260
316
|
if (resumedPayment.status === 'succeeded') {
|
|
261
317
|
this.callbacks.onProcessing?.(false);
|
|
262
318
|
this.callbacks.onSuccess?.(resumedPayment);
|
|
263
|
-
return;
|
|
319
|
+
return { kind: 'completed', payment: resumedPayment };
|
|
264
320
|
}
|
|
265
321
|
if (resumedPayment.requireAction !== 'none' && resumedPayment.requireActionData) {
|
|
266
|
-
|
|
267
|
-
return;
|
|
322
|
+
return this.handlePaymentAction(resumedPayment);
|
|
268
323
|
}
|
|
269
|
-
// Start polling for final status
|
|
324
|
+
// Start polling for final status — outcome will arrive via callbacks.
|
|
270
325
|
this.startPolling(resumedPayment.id, {
|
|
271
326
|
onSuccess: (p) => {
|
|
272
327
|
this.callbacks.onProcessing?.(false);
|
|
@@ -278,18 +333,19 @@ export class PaymentService {
|
|
|
278
333
|
},
|
|
279
334
|
onRequireAction: (p) => { void this.handlePaymentAction(p); },
|
|
280
335
|
});
|
|
336
|
+
return { kind: 'pending' };
|
|
281
337
|
}
|
|
282
338
|
// ==========================================================================
|
|
283
339
|
// PAYMENT ACTION HANDLER (mirrors usePaymentActionHandler)
|
|
284
340
|
// ==========================================================================
|
|
285
341
|
async handlePaymentAction(payment) {
|
|
286
342
|
if (payment.requireAction === 'none')
|
|
287
|
-
return;
|
|
343
|
+
return { kind: 'pending' };
|
|
288
344
|
if (payment.requireActionData?.processed)
|
|
289
|
-
return;
|
|
345
|
+
return { kind: 'pending' };
|
|
290
346
|
const actionData = payment.requireActionData;
|
|
291
347
|
if (!actionData)
|
|
292
|
-
return;
|
|
348
|
+
return { kind: 'pending' };
|
|
293
349
|
try {
|
|
294
350
|
await this.paymentsResource.markPaymentActionProcessed(payment.id);
|
|
295
351
|
}
|
|
@@ -301,48 +357,59 @@ export class PaymentService {
|
|
|
301
357
|
const redirectUrl = actionData.metadata?.redirect?.redirectUrl || actionData.redirectUrl || actionData.url;
|
|
302
358
|
if (redirectUrl) {
|
|
303
359
|
console.log('[PaymentService] Redirecting:', redirectUrl);
|
|
360
|
+
if (this.callbacks.onBeforeRedirect) {
|
|
361
|
+
await this.callbacks.onBeforeRedirect(payment, redirectUrl);
|
|
362
|
+
}
|
|
304
363
|
window.location.href = redirectUrl;
|
|
364
|
+
return { kind: 'redirected' };
|
|
305
365
|
}
|
|
306
|
-
|
|
366
|
+
if (payment.status === 'succeeded') {
|
|
307
367
|
this.callbacks.onProcessing?.(false);
|
|
308
368
|
this.callbacks.onSuccess?.(payment);
|
|
369
|
+
return { kind: 'completed', payment };
|
|
309
370
|
}
|
|
310
|
-
|
|
371
|
+
// Action said redirect but no URL and payment not succeeded — broken
|
|
372
|
+
// response from backend. Surface as failure rather than hanging.
|
|
373
|
+
const noUrlMsg = 'Payment redirect URL missing';
|
|
374
|
+
this.callbacks.onError?.(noUrlMsg);
|
|
375
|
+
this.callbacks.onProcessing?.(false);
|
|
376
|
+
return { kind: 'failed', error: noUrlMsg, payment };
|
|
311
377
|
}
|
|
312
378
|
case 'threeds_auth': {
|
|
313
379
|
const session = actionData.metadata?.threedsSession;
|
|
314
380
|
if (session?.acsChallengeUrl) {
|
|
315
381
|
console.log('[PaymentService] 3DS challenge redirect:', session.acsChallengeUrl);
|
|
316
382
|
window.location.href = session.acsChallengeUrl;
|
|
383
|
+
return { kind: 'redirected' };
|
|
317
384
|
}
|
|
318
|
-
|
|
385
|
+
const noUrlMsg = '3DS challenge URL missing';
|
|
386
|
+
this.callbacks.onError?.(noUrlMsg);
|
|
387
|
+
this.callbacks.onProcessing?.(false);
|
|
388
|
+
return { kind: 'failed', error: noUrlMsg, payment };
|
|
319
389
|
}
|
|
320
390
|
case 'error': {
|
|
321
391
|
const msg = actionData.message || 'Payment action failed';
|
|
322
392
|
this.callbacks.onError?.(msg);
|
|
323
393
|
this.callbacks.onProcessing?.(false);
|
|
324
|
-
|
|
394
|
+
return { kind: 'failed', error: msg, payment };
|
|
325
395
|
}
|
|
326
396
|
case 'kesspay_auth':
|
|
327
|
-
this.handleKessPayAuth(actionData);
|
|
328
|
-
break;
|
|
397
|
+
return this.handleKessPayAuth(actionData);
|
|
329
398
|
case 'trustflow_auth':
|
|
330
|
-
this.handleTrustFlowAuth(actionData);
|
|
331
|
-
break;
|
|
399
|
+
return this.handleTrustFlowAuth(actionData);
|
|
332
400
|
case 'finix_radar':
|
|
333
|
-
|
|
334
|
-
break;
|
|
401
|
+
return this.handleFinixRadar(payment, actionData);
|
|
335
402
|
case 'stripe_radar':
|
|
336
|
-
|
|
337
|
-
break;
|
|
403
|
+
return this.handleStripeRadar(payment, actionData);
|
|
338
404
|
case 'radar':
|
|
339
405
|
if (actionData.metadata?.provider === 'airwallex') {
|
|
340
|
-
|
|
406
|
+
return this.handleAirwallexRadar(payment, actionData);
|
|
341
407
|
}
|
|
342
|
-
|
|
408
|
+
return { kind: 'pending' };
|
|
343
409
|
case 'mastercard_auth':
|
|
344
|
-
|
|
345
|
-
|
|
410
|
+
return this.handleMasterCardAuth(payment, actionData);
|
|
411
|
+
case 'ngenius_3ds':
|
|
412
|
+
return this.handleNgeniusThreeds(payment, actionData);
|
|
346
413
|
default: {
|
|
347
414
|
console.log('[PaymentService] Unhandled action, starting polling:', actionData.type);
|
|
348
415
|
this.startPolling(payment.id, {
|
|
@@ -350,7 +417,7 @@ export class PaymentService {
|
|
|
350
417
|
onFailure: (e) => { this.callbacks.onError?.(e); this.callbacks.onProcessing?.(false); },
|
|
351
418
|
onRequireAction: (p) => { void this.handlePaymentAction(p); },
|
|
352
419
|
});
|
|
353
|
-
|
|
420
|
+
return { kind: 'pending' };
|
|
354
421
|
}
|
|
355
422
|
}
|
|
356
423
|
}
|
|
@@ -360,9 +427,10 @@ export class PaymentService {
|
|
|
360
427
|
handleKessPayAuth(actionData) {
|
|
361
428
|
const threeDSData = actionData?.metadata?.threeds;
|
|
362
429
|
if (!threeDSData?.challengeHtml) {
|
|
363
|
-
|
|
430
|
+
const msg = 'Missing KessPay 3DS challenge HTML';
|
|
431
|
+
this.callbacks.onError?.(msg);
|
|
364
432
|
this.callbacks.onProcessing?.(false);
|
|
365
|
-
return;
|
|
433
|
+
return { kind: 'failed', error: msg };
|
|
366
434
|
}
|
|
367
435
|
try {
|
|
368
436
|
this.callbacks.onProcessing?.(false);
|
|
@@ -389,10 +457,13 @@ export class PaymentService {
|
|
|
389
457
|
document.write(threeDSData.challengeHtml);
|
|
390
458
|
document.close();
|
|
391
459
|
}
|
|
460
|
+
return { kind: 'redirected' };
|
|
392
461
|
}
|
|
393
462
|
catch (error) {
|
|
394
|
-
|
|
463
|
+
const msg = error instanceof Error ? error.message : 'KessPay 3DS failed';
|
|
464
|
+
this.callbacks.onError?.(msg);
|
|
395
465
|
this.callbacks.onProcessing?.(false);
|
|
466
|
+
return { kind: 'failed', error: msg };
|
|
396
467
|
}
|
|
397
468
|
}
|
|
398
469
|
// --------------------------------------------------------------------------
|
|
@@ -401,9 +472,10 @@ export class PaymentService {
|
|
|
401
472
|
handleTrustFlowAuth(actionData) {
|
|
402
473
|
const authData = actionData?.metadata?.trustflow;
|
|
403
474
|
if (!authData?.appId || !authData?.txnId || !authData?.hash) {
|
|
404
|
-
|
|
475
|
+
const msg = 'Missing Trust Flow 3DS data';
|
|
476
|
+
this.callbacks.onError?.(msg);
|
|
405
477
|
this.callbacks.onProcessing?.(false);
|
|
406
|
-
return;
|
|
478
|
+
return { kind: 'failed', error: msg };
|
|
407
479
|
}
|
|
408
480
|
try {
|
|
409
481
|
this.callbacks.onProcessing?.(false);
|
|
@@ -420,10 +492,13 @@ export class PaymentService {
|
|
|
420
492
|
}
|
|
421
493
|
document.body.appendChild(form);
|
|
422
494
|
form.submit();
|
|
495
|
+
return { kind: 'redirected' };
|
|
423
496
|
}
|
|
424
497
|
catch (error) {
|
|
425
|
-
|
|
498
|
+
const msg = error instanceof Error ? error.message : 'Trust Flow 3DS failed';
|
|
499
|
+
this.callbacks.onError?.(msg);
|
|
426
500
|
this.callbacks.onProcessing?.(false);
|
|
501
|
+
return { kind: 'failed', error: msg };
|
|
427
502
|
}
|
|
428
503
|
}
|
|
429
504
|
// --------------------------------------------------------------------------
|
|
@@ -432,9 +507,10 @@ export class PaymentService {
|
|
|
432
507
|
async handleFinixRadar(payment, actionData) {
|
|
433
508
|
const radarConfig = actionData.metadata?.radar;
|
|
434
509
|
if (!radarConfig) {
|
|
435
|
-
|
|
510
|
+
const msg = 'Finix radar config missing';
|
|
511
|
+
this.callbacks.onError?.(msg);
|
|
436
512
|
this.callbacks.onProcessing?.(false);
|
|
437
|
-
return;
|
|
513
|
+
return { kind: 'failed', error: msg, payment };
|
|
438
514
|
}
|
|
439
515
|
try {
|
|
440
516
|
await this.loadScript('https://js.finix.com/v/1/finix.js', () => typeof window.Finix?.Auth === 'function');
|
|
@@ -462,11 +538,13 @@ export class PaymentService {
|
|
|
462
538
|
},
|
|
463
539
|
});
|
|
464
540
|
const resumed = await this.paymentsResource.completePaymentAfterAction(payment.id);
|
|
465
|
-
await this.handleResumedPayment(resumed);
|
|
541
|
+
return await this.handleResumedPayment(resumed);
|
|
466
542
|
}
|
|
467
543
|
catch (error) {
|
|
468
|
-
|
|
544
|
+
const msg = error instanceof Error ? error.message : 'Finix radar failed';
|
|
545
|
+
this.callbacks.onError?.(msg);
|
|
469
546
|
this.callbacks.onProcessing?.(false);
|
|
547
|
+
return { kind: 'failed', error: msg, payment };
|
|
470
548
|
}
|
|
471
549
|
}
|
|
472
550
|
// --------------------------------------------------------------------------
|
|
@@ -475,9 +553,10 @@ export class PaymentService {
|
|
|
475
553
|
async handleStripeRadar(payment, actionData) {
|
|
476
554
|
const radarConfig = actionData.metadata?.radar;
|
|
477
555
|
if (!radarConfig?.publishableKey) {
|
|
478
|
-
|
|
556
|
+
const msg = 'Stripe radar config missing';
|
|
557
|
+
this.callbacks.onError?.(msg);
|
|
479
558
|
this.callbacks.onProcessing?.(false);
|
|
480
|
-
return;
|
|
559
|
+
return { kind: 'failed', error: msg, payment };
|
|
481
560
|
}
|
|
482
561
|
try {
|
|
483
562
|
await this.loadScript('https://js.stripe.com/v3/', () => typeof window.Stripe === 'function');
|
|
@@ -493,11 +572,60 @@ export class PaymentService {
|
|
|
493
572
|
stripeRadarSessionData: result.radarSession,
|
|
494
573
|
});
|
|
495
574
|
const resumed = await this.paymentsResource.completePaymentAfterAction(payment.id);
|
|
496
|
-
await this.handleResumedPayment(resumed);
|
|
575
|
+
return await this.handleResumedPayment(resumed);
|
|
497
576
|
}
|
|
498
577
|
catch (error) {
|
|
499
|
-
|
|
578
|
+
const msg = error instanceof Error ? error.message : 'Stripe radar failed';
|
|
579
|
+
this.callbacks.onError?.(msg);
|
|
500
580
|
this.callbacks.onProcessing?.(false);
|
|
581
|
+
return { kind: 'failed', error: msg, payment };
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
// --------------------------------------------------------------------------
|
|
585
|
+
// N-Genius 3DS (WebSDK)
|
|
586
|
+
// --------------------------------------------------------------------------
|
|
587
|
+
async handleNgeniusThreeds(payment, actionData) {
|
|
588
|
+
const sdk = actionData.metadata?.sdk;
|
|
589
|
+
if (!sdk?.paymentResponse || !sdk.orderReference || !sdk.paymentReference) {
|
|
590
|
+
const msg = 'N-Genius 3DS: missing SDK metadata';
|
|
591
|
+
this.callbacks.onError?.(msg);
|
|
592
|
+
this.callbacks.onProcessing?.(false);
|
|
593
|
+
return { kind: 'failed', error: msg, payment };
|
|
594
|
+
}
|
|
595
|
+
try {
|
|
596
|
+
const sdkUrl = sdk.isSandboxed
|
|
597
|
+
? 'https://paypage.sandbox.ngenius-payments.com/hosted-sessions/sdk.js'
|
|
598
|
+
: 'https://paypage.ngenius-payments.com/hosted-sessions/sdk.js';
|
|
599
|
+
if (!document.getElementById('ngenius-websdk')) {
|
|
600
|
+
await new Promise((resolve, reject) => {
|
|
601
|
+
const script = document.createElement('script');
|
|
602
|
+
script.id = 'ngenius-websdk';
|
|
603
|
+
script.src = sdkUrl;
|
|
604
|
+
script.onload = () => resolve();
|
|
605
|
+
script.onerror = () => reject(new Error('Failed to load N-Genius WebSDK'));
|
|
606
|
+
document.head.appendChild(script);
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
const NI = window.NI;
|
|
610
|
+
if (!NI?.handlePaymentResponse) {
|
|
611
|
+
throw new Error('N-Genius WebSDK did not expose window.NI.handlePaymentResponse');
|
|
612
|
+
}
|
|
613
|
+
console.log('[N-Genius 3DS] Starting WebSDK challenge');
|
|
614
|
+
const outcome = await NI.handlePaymentResponse(sdk.paymentResponse, { mountId: 'ngenius-3ds-container', style: { width: '100%', height: 500 } });
|
|
615
|
+
console.log('[N-Genius 3DS] WebSDK outcome:', outcome.status);
|
|
616
|
+
const completedPayment = await this.paymentsResource.ngeniusThreedsComplete({
|
|
617
|
+
paymentId: payment.id,
|
|
618
|
+
orderReference: sdk.orderReference,
|
|
619
|
+
paymentReference: sdk.paymentReference,
|
|
620
|
+
});
|
|
621
|
+
return await this.handleResumedPayment(completedPayment);
|
|
622
|
+
}
|
|
623
|
+
catch (error) {
|
|
624
|
+
const msg = error instanceof Error ? error.message : 'N-Genius 3DS failed';
|
|
625
|
+
console.error('[N-Genius 3DS] Error:', error);
|
|
626
|
+
this.callbacks.onError?.(msg);
|
|
627
|
+
this.callbacks.onProcessing?.(false);
|
|
628
|
+
return { kind: 'failed', error: msg, payment };
|
|
501
629
|
}
|
|
502
630
|
}
|
|
503
631
|
// --------------------------------------------------------------------------
|
|
@@ -508,9 +636,10 @@ export class PaymentService {
|
|
|
508
636
|
const orderId = payment.order?.id;
|
|
509
637
|
const checkoutSessionId = payment.order?.checkoutSessionId;
|
|
510
638
|
if (!orderId || !checkoutSessionId) {
|
|
511
|
-
|
|
639
|
+
const msg = 'Missing order info for Airwallex radar';
|
|
640
|
+
this.callbacks.onError?.(msg);
|
|
512
641
|
this.callbacks.onProcessing?.(false);
|
|
513
|
-
return;
|
|
642
|
+
return { kind: 'failed', error: msg, payment };
|
|
514
643
|
}
|
|
515
644
|
try {
|
|
516
645
|
const sessionId = crypto.randomUUID();
|
|
@@ -539,11 +668,13 @@ export class PaymentService {
|
|
|
539
668
|
airwallexRadarSessionId: sessionId,
|
|
540
669
|
});
|
|
541
670
|
const resumed = await this.paymentsResource.completePaymentAfterAction(payment.id);
|
|
542
|
-
await this.handleResumedPayment(resumed);
|
|
671
|
+
return await this.handleResumedPayment(resumed);
|
|
543
672
|
}
|
|
544
673
|
catch (error) {
|
|
545
|
-
|
|
674
|
+
const msg = error instanceof Error ? error.message : 'Airwallex radar failed';
|
|
675
|
+
this.callbacks.onError?.(msg);
|
|
546
676
|
this.callbacks.onProcessing?.(false);
|
|
677
|
+
return { kind: 'failed', error: msg, payment };
|
|
547
678
|
}
|
|
548
679
|
}
|
|
549
680
|
// --------------------------------------------------------------------------
|
|
@@ -552,9 +683,10 @@ export class PaymentService {
|
|
|
552
683
|
async handleMasterCardAuth(payment, actionData) {
|
|
553
684
|
const threeDSData = actionData?.metadata?.threeds;
|
|
554
685
|
if (!threeDSData?.sessionId || !threeDSData?.merchantId) {
|
|
555
|
-
|
|
686
|
+
const msg = 'Missing MasterCard 3DS data';
|
|
687
|
+
this.callbacks.onError?.(msg);
|
|
556
688
|
this.callbacks.onProcessing?.(false);
|
|
557
|
-
return;
|
|
689
|
+
return { kind: 'failed', error: msg, payment };
|
|
558
690
|
}
|
|
559
691
|
try {
|
|
560
692
|
this.callbacks.onProcessing?.(false);
|
|
@@ -607,21 +739,26 @@ export class PaymentService {
|
|
|
607
739
|
document.write(challengeHtml);
|
|
608
740
|
document.close();
|
|
609
741
|
}
|
|
742
|
+
return { kind: 'redirected' };
|
|
610
743
|
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
744
|
+
// Frictionless — complete immediately via resumed payment
|
|
745
|
+
if (threeDSData.paymentId) {
|
|
746
|
+
const resumed = await this.paymentsResource.completePaymentAfterAction(threeDSData.paymentId);
|
|
747
|
+
const cleanup = document.getElementById(containerId);
|
|
748
|
+
if (cleanup)
|
|
749
|
+
cleanup.remove();
|
|
750
|
+
return await this.handleResumedPayment(resumed);
|
|
617
751
|
}
|
|
618
752
|
const cleanup = document.getElementById(containerId);
|
|
619
753
|
if (cleanup)
|
|
620
754
|
cleanup.remove();
|
|
755
|
+
return { kind: 'pending' };
|
|
621
756
|
}
|
|
622
757
|
catch (error) {
|
|
623
|
-
|
|
758
|
+
const msg = error instanceof Error ? error.message : 'MasterCard 3DS failed';
|
|
759
|
+
this.callbacks.onError?.(msg);
|
|
624
760
|
this.callbacks.onProcessing?.(false);
|
|
761
|
+
return { kind: 'failed', error: msg, payment };
|
|
625
762
|
}
|
|
626
763
|
}
|
|
627
764
|
// --------------------------------------------------------------------------
|
|
@@ -835,7 +972,7 @@ export class PaymentService {
|
|
|
835
972
|
// ==========================================================================
|
|
836
973
|
// PAYMENT PROCESSING (all methods)
|
|
837
974
|
// ==========================================================================
|
|
838
|
-
async processCardPayment(checkoutSessionId, cardData) {
|
|
975
|
+
async processCardPayment(checkoutSessionId, cardData, options) {
|
|
839
976
|
this.callbacks.onProcessing?.(true);
|
|
840
977
|
this.callbacks.onError?.(null);
|
|
841
978
|
try {
|
|
@@ -858,7 +995,7 @@ export class PaymentService {
|
|
|
858
995
|
// Continue without 3DS
|
|
859
996
|
}
|
|
860
997
|
}
|
|
861
|
-
return await this.processAndHandle(checkoutSessionId, instrument.id, threedsSessionId);
|
|
998
|
+
return await this.processAndHandle(checkoutSessionId, instrument.id, threedsSessionId, { shippingRateId: options?.shippingRateId });
|
|
862
999
|
}
|
|
863
1000
|
catch (error) {
|
|
864
1001
|
const msg = error instanceof Error ? error.message : String(error);
|
|
@@ -867,12 +1004,12 @@ export class PaymentService {
|
|
|
867
1004
|
return { success: false, error: msg };
|
|
868
1005
|
}
|
|
869
1006
|
}
|
|
870
|
-
async processApplePayPayment(checkoutSessionId, applePayToken) {
|
|
1007
|
+
async processApplePayPayment(checkoutSessionId, applePayToken, options) {
|
|
871
1008
|
this.callbacks.onProcessing?.(true);
|
|
872
1009
|
this.callbacks.onError?.(null);
|
|
873
1010
|
try {
|
|
874
1011
|
const instrument = await this.createApplePayPaymentInstrument(applePayToken);
|
|
875
|
-
return await this.processAndHandle(checkoutSessionId, instrument.id);
|
|
1012
|
+
return await this.processAndHandle(checkoutSessionId, instrument.id, undefined, { shippingRateId: options?.shippingRateId });
|
|
876
1013
|
}
|
|
877
1014
|
catch (error) {
|
|
878
1015
|
const msg = error instanceof Error ? error.message : String(error);
|
|
@@ -881,12 +1018,12 @@ export class PaymentService {
|
|
|
881
1018
|
return { success: false, error: msg };
|
|
882
1019
|
}
|
|
883
1020
|
}
|
|
884
|
-
async processGooglePayPayment(checkoutSessionId, googlePayToken) {
|
|
1021
|
+
async processGooglePayPayment(checkoutSessionId, googlePayToken, options) {
|
|
885
1022
|
this.callbacks.onProcessing?.(true);
|
|
886
1023
|
this.callbacks.onError?.(null);
|
|
887
1024
|
try {
|
|
888
1025
|
const instrument = await this.createGooglePayPaymentInstrument(googlePayToken);
|
|
889
|
-
return await this.processAndHandle(checkoutSessionId, instrument.id);
|
|
1026
|
+
return await this.processAndHandle(checkoutSessionId, instrument.id, undefined, { shippingRateId: options?.shippingRateId });
|
|
890
1027
|
}
|
|
891
1028
|
catch (error) {
|
|
892
1029
|
const msg = error instanceof Error ? error.message : String(error);
|
|
@@ -895,11 +1032,11 @@ export class PaymentService {
|
|
|
895
1032
|
return { success: false, error: msg };
|
|
896
1033
|
}
|
|
897
1034
|
}
|
|
898
|
-
async processPaymentWithInstrument(checkoutSessionId, paymentInstrumentId) {
|
|
1035
|
+
async processPaymentWithInstrument(checkoutSessionId, paymentInstrumentId, options) {
|
|
899
1036
|
this.callbacks.onProcessing?.(true);
|
|
900
1037
|
this.callbacks.onError?.(null);
|
|
901
1038
|
try {
|
|
902
|
-
return await this.processAndHandle(checkoutSessionId, paymentInstrumentId);
|
|
1039
|
+
return await this.processAndHandle(checkoutSessionId, paymentInstrumentId, undefined, { shippingRateId: options?.shippingRateId });
|
|
903
1040
|
}
|
|
904
1041
|
catch (error) {
|
|
905
1042
|
const msg = error instanceof Error ? error.message : String(error);
|
|
@@ -908,7 +1045,7 @@ export class PaymentService {
|
|
|
908
1045
|
return { success: false, error: msg };
|
|
909
1046
|
}
|
|
910
1047
|
}
|
|
911
|
-
async processApmPayment(checkoutSessionId, apmData) {
|
|
1048
|
+
async processApmPayment(checkoutSessionId, apmData, options) {
|
|
912
1049
|
this.callbacks.onProcessing?.(true);
|
|
913
1050
|
this.callbacks.onError?.(null);
|
|
914
1051
|
try {
|
|
@@ -917,6 +1054,89 @@ export class PaymentService {
|
|
|
917
1054
|
paymentMethod: apmData.paymentMethod,
|
|
918
1055
|
initiatedBy: apmData.initiatedBy,
|
|
919
1056
|
source: apmData.source,
|
|
1057
|
+
shippingRateId: options?.shippingRateId,
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
catch (error) {
|
|
1061
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
1062
|
+
this.callbacks.onError?.(msg);
|
|
1063
|
+
this.callbacks.onProcessing?.(false);
|
|
1064
|
+
return { success: false, error: msg };
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* Stripe Express Checkout Element payment.
|
|
1069
|
+
*
|
|
1070
|
+
* Mirrors the inline flow from `react/components/StripeExpressButton.onConfirm`:
|
|
1071
|
+
* 1. processPaymentDirect with isExpress=true → returns clientSecret
|
|
1072
|
+
* 2. stripe.confirmPayment(elements, clientSecret) — must run while wallet sheet is open
|
|
1073
|
+
* 3. Poll until webhook marks payment succeeded
|
|
1074
|
+
*
|
|
1075
|
+
* Used for ECE methods Stripe surfaces in one element: apple_pay, google_pay, link, klarna.
|
|
1076
|
+
* The `stripe` and `elements` refs come from Stripe React hooks at the call site.
|
|
1077
|
+
*/
|
|
1078
|
+
async processStripeExpressPayment(checkoutSessionId, paymentMethod, processorId, stripe, elements, options) {
|
|
1079
|
+
this.callbacks.onProcessing?.(true);
|
|
1080
|
+
this.callbacks.onError?.(null);
|
|
1081
|
+
try {
|
|
1082
|
+
const paymentFlowId = getAssignedPaymentFlowId();
|
|
1083
|
+
const response = await this.paymentsResource.processPaymentDirect(checkoutSessionId, '', undefined, {
|
|
1084
|
+
processorId,
|
|
1085
|
+
paymentMethod,
|
|
1086
|
+
isExpress: true,
|
|
1087
|
+
paymentFlowId,
|
|
1088
|
+
shippingRateId: options?.shippingRateId,
|
|
1089
|
+
});
|
|
1090
|
+
this.callbacks.onCurrentPaymentId?.(response.payment?.id || null);
|
|
1091
|
+
const clientSecret = response?.payment?.requireActionData?.metadata?.stripeExpressCheckout?.clientSecret;
|
|
1092
|
+
if (!clientSecret) {
|
|
1093
|
+
const msg = 'Express checkout configuration missing — no client secret returned';
|
|
1094
|
+
this.callbacks.onError?.(msg);
|
|
1095
|
+
this.callbacks.onProcessing?.(false);
|
|
1096
|
+
return { success: false, error: msg, payment: response.payment, order: response.order };
|
|
1097
|
+
}
|
|
1098
|
+
const { error: confirmError } = await stripe.confirmPayment({
|
|
1099
|
+
elements,
|
|
1100
|
+
clientSecret,
|
|
1101
|
+
confirmParams: { return_url: window.location.href },
|
|
1102
|
+
redirect: 'if_required',
|
|
1103
|
+
});
|
|
1104
|
+
if (confirmError) {
|
|
1105
|
+
const msg = confirmError.message ?? 'Payment confirmation failed';
|
|
1106
|
+
this.callbacks.onError?.(msg);
|
|
1107
|
+
this.callbacks.onProcessing?.(false);
|
|
1108
|
+
return { success: false, error: msg, payment: response.payment, order: response.order };
|
|
1109
|
+
}
|
|
1110
|
+
// Poll for webhook completion. Ignore stripe_express_checkout require-actions
|
|
1111
|
+
// (already handled by stripe.confirmPayment above) and keep polling.
|
|
1112
|
+
const paymentId = response.payment.id;
|
|
1113
|
+
return await new Promise((resolve) => {
|
|
1114
|
+
const tick = async () => {
|
|
1115
|
+
try {
|
|
1116
|
+
const payment = await this.paymentsResource.getPaymentStatus(paymentId);
|
|
1117
|
+
if (payment.status === 'succeeded' ||
|
|
1118
|
+
(payment.status === 'pending' && payment.subStatus === 'authorized')) {
|
|
1119
|
+
this.callbacks.onProcessing?.(false);
|
|
1120
|
+
resolve({ success: true, payment, order: response.order });
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
if (payment.status !== 'succeeded' && payment.status !== 'pending') {
|
|
1124
|
+
const msg = payment.status || 'Payment failed';
|
|
1125
|
+
this.callbacks.onError?.(msg);
|
|
1126
|
+
this.callbacks.onProcessing?.(false);
|
|
1127
|
+
resolve({ success: false, error: msg, payment, order: response.order });
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
// Pending — keep polling. Ignore stripe_express_checkout require-action
|
|
1131
|
+
// (it's just the original intent metadata; we already handled it).
|
|
1132
|
+
setTimeout(tick, 1500);
|
|
1133
|
+
}
|
|
1134
|
+
catch {
|
|
1135
|
+
// Network blip — try again. Bail after a few failures handled by getPaymentStatus retries upstream.
|
|
1136
|
+
setTimeout(tick, 1500);
|
|
1137
|
+
}
|
|
1138
|
+
};
|
|
1139
|
+
void tick();
|
|
920
1140
|
});
|
|
921
1141
|
}
|
|
922
1142
|
catch (error) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tagadapay/plugin-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.2",
|
|
4
4
|
"description": "Modern React SDK for building Tagada Pay plugins",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
"@google-pay/button-react": "^3.0.10",
|
|
77
77
|
"@stripe/react-stripe-js": "^5.6.1",
|
|
78
78
|
"@stripe/stripe-js": "^8.11.0",
|
|
79
|
+
"@tagadapay/core-js": "workspace:*",
|
|
79
80
|
"@tagadapay/plugin-sdk": "link:",
|
|
80
81
|
"@tanstack/react-query": "^5.90.2",
|
|
81
82
|
"@whop/checkout": "^0.0.40",
|