@salla.sa/applepay 2.14.569 → 2.14.571

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.
Files changed (2) hide show
  1. package/package.json +2 -2
  2. package/src/index.js +109 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salla.sa/applepay",
3
- "version": "2.14.569",
3
+ "version": "2.14.571",
4
4
  "description": "Salla Apple Pay light",
5
5
  "main": "dist/app.js",
6
6
  "scripts": {
@@ -26,5 +26,5 @@
26
26
  "dependencies": {
27
27
  "axios": "^1.10.0"
28
28
  },
29
- "gitHead": "3c0815da0b6b3cfcb24245834f09ed9d96a29c14"
29
+ "gitHead": "6ca1f94b0197d7a671c7412e27162e01aa2186b7"
30
30
  }
package/src/index.js CHANGED
@@ -156,6 +156,18 @@ const SallaApplePay = typeof window !== 'undefined' ? {
156
156
  // Fresh transaction → clear any cancel flag from a previous attempt.
157
157
  SallaApplePay.canceled = false;
158
158
 
159
+ // Analytics latches — one Submitted and one Succeeded per attempt, and
160
+ // at most one Failed, however many callbacks fire.
161
+ SallaApplePay._submittedTracked = false;
162
+ SallaApplePay._failureTracked = false;
163
+ SallaApplePay._succeededTracked = false;
164
+
165
+ // The cart belongs to the attempt, not to the singleton. The mini embed
166
+ // supplies one up front; on the PDP it stays null until checkout/mini
167
+ // answers during merchant validation. Without this a second attempt
168
+ // reports — and submits to — the previous attempt's cart.
169
+ SallaApplePay.id = SallaApplePay.detail?.cartId || null;
170
+
159
171
  salla.log('🍏 Pay: payments::apple-pay.start-transaction', SallaApplePay.detail);
160
172
 
161
173
  SallaApplePay.initDefault();
@@ -220,8 +232,17 @@ const SallaApplePay = typeof window !== 'undefined' ? {
220
232
  skipBegin = SallaApplePay.detail.onStartedSession(SallaApplePay.session);
221
233
  }
222
234
  if (!skipBegin) {
223
- SallaApplePay.session.begin();
235
+ try {
236
+ SallaApplePay.session.begin();
237
+ } catch (error) {
238
+ salla.logger.error('🍏 Pay: session.begin() failed', error);
239
+ SallaApplePay.trackPaymentFailed('client', 'APPLE_PAY_PRESENTATION_FAILED', error?.message || '');
240
+ SallaApplePay.detail.onError(salla.lang.get('pages.checkout.payment_failed'));
241
+ Salla.event.createAndDispatch('payments::apple-pay.canceled', {});
242
+ }
224
243
  }
244
+ // skipBegin path: the caller begins later — onValidateMerchant fires
245
+ // once the sheet actually opens and emits the (latched) Submitted.
225
246
  },
226
247
  onPaymentMethodSelected: async (event) => {
227
248
  salla.logger.log('🍏 Pay: onPaymentMethodSelected', event);
@@ -306,6 +327,10 @@ const SallaApplePay = typeof window !== 'undefined' ? {
306
327
 
307
328
  onCancel: (event = {}, message = null) => {
308
329
  SallaApplePay.canceled = true;
330
+ // Sheet opened (Payment Submitted already fired) but the attempt ended
331
+ // without authorization — dismissal, abort, or validation failure.
332
+ // Latched: a server rejection tracked earlier absorbs this call.
333
+ SallaApplePay.trackPaymentFailed('client', 'APPLE_PAY_SESSION_CANCELED', message || '');
309
334
  SallaApplePay.detail.onError(message || salla.lang.get('pages.checkout.payment_failed'));
310
335
  Salla.event.createAndDispatch('payments::apple-pay.canceled', event);
311
336
  },
@@ -367,11 +392,15 @@ const SallaApplePay = typeof window !== 'undefined' ? {
367
392
  }
368
393
 
369
394
  Salla.event.dispatch('payments::apple-pay.authorized.init', event);
395
+ // The shopper has authorised with Face ID / Touch ID and the token is
396
+ // about to go to the gateway — this is the submission, not the tap.
397
+ SallaApplePay.trackPaymentSubmitted();
370
398
  http.post(SallaApplePay.detail.authorized.url.replace('{id}', SallaApplePay.id), {
371
399
  payment_method: 'apple_pay',
372
400
  applepay_token: JSON.stringify(event.payment)
373
401
  }, ({ data }) => {
374
402
  Salla.event.dispatch('payments::apple-pay.authorized.success', data);
403
+ SallaApplePay.trackPaymentSucceeded(data);
375
404
 
376
405
  if (typeof SallaApplePay.detail.authorized.onSuccess === 'function') {
377
406
  SallaApplePay.detail.authorized.onSuccess(data);
@@ -383,6 +412,11 @@ const SallaApplePay = typeof window !== 'undefined' ? {
383
412
  let response = error?.response;
384
413
 
385
414
  Salla.event.dispatch('payments::apple-pay.authorized.failed', response);
415
+ // Backend rejected the token — latch set so the cancel callback
416
+ // following the failure UI can't emit a second Failed.
417
+ SallaApplePay.trackPaymentFailed('server',
418
+ response?.data?.error?.code || 'APPLE_PAY_FAILED',
419
+ response?.data?.error?.message || '');
386
420
 
387
421
  if (typeof SallaApplePay.detail.authorized.onFailed === 'function') {
388
422
  SallaApplePay.detail.authorized.onFailed(response);
@@ -849,6 +883,72 @@ const SallaApplePay = typeof window !== 'undefined' ? {
849
883
  };
850
884
  },
851
885
 
886
+ track: function (event, extra = {}) {
887
+ const id = SallaApplePay.id ? String(SallaApplePay.id) : null;
888
+ try {
889
+ salla.analytics?.track(event, {
890
+ payment_method: 'apple_pay',
891
+ gateway: 'apple_pay',
892
+ cart_id: id,
893
+ checkout_id: id,
894
+ session_id: salla.config?.get?.('jitsu.session_id') || null,
895
+ ...extra,
896
+ // The host knows the surface — PDP quick-buy vs the mini-checkout
897
+ // embed both render salla-quick-buy and share this package.
898
+ checkout_type: extra.checkout_type
899
+ || SallaApplePay.detail?.checkout_type
900
+ || 'applepay_checkout',
901
+ });
902
+ } catch (error) {
903
+ salla.logger.error('🍏 Pay: failed to track ' + event, error);
904
+ }
905
+ },
906
+
907
+ trackPaymentSubmitted: function () {
908
+ if (SallaApplePay._submittedTracked) {
909
+ return;
910
+ }
911
+ SallaApplePay._submittedTracked = true;
912
+ // Checkout Started belongs to the host: the mini-checkout embed already
913
+ // emits its own on open, and on the PDP the cart does not exist until
914
+ // checkout/mini answers, so salla-quick-buy fires it there with a real id.
915
+ SallaApplePay.track('Payment Submitted', {});
916
+ },
917
+
918
+ trackPaymentFailed: function (errorType, errorCode, message = '') {
919
+ // Payment Failed answers a Payment Submitted, and Submitted is emitted at
920
+ // authorization. Anything that ends the attempt before that — a dismissed
921
+ // sheet, a refused presentation, merchant validation — is abandonment or a
922
+ // setup error, not a failed payment, and counting it here would make
923
+ // Failed exceed Submitted. Those paths still log, still call
924
+ // detail.onError and still dispatch payments::apple-pay.canceled, so
925
+ // hosts and error reporting are unaffected; only the funnel is.
926
+ if (!SallaApplePay._submittedTracked || SallaApplePay._failureTracked) {
927
+ return;
928
+ }
929
+ SallaApplePay._failureTracked = true;
930
+ SallaApplePay.track('Payment Failed', {
931
+ error_type: errorType,
932
+ error_code: errorCode,
933
+ error_message: message || '',
934
+ });
935
+ },
936
+
937
+ trackPaymentSucceeded: function (data) {
938
+ if (SallaApplePay._succeededTracked) {
939
+ return;
940
+ }
941
+ SallaApplePay._succeededTracked = true;
942
+ const payload = data?.data || data || {};
943
+ SallaApplePay.track('Payment Succeeded', {
944
+ order_id: payload.order_id || null,
945
+ cart_total: payload.total || SallaApplePay.detail?.amount || null,
946
+ response_status: 'success',
947
+ has_redirect: false,
948
+ redirect_url: null,
949
+ });
950
+ },
951
+
852
952
  /** Authorize + submit the payment, then close the sheet. */
853
953
  prPaymentAuthorized: async function (response) {
854
954
  // Guest checkout → persist the payer contact before submitting.
@@ -886,6 +986,8 @@ const SallaApplePay = typeof window !== 'undefined' ? {
886
986
  }
887
987
 
888
988
  Salla.event.dispatch('payments::apple-pay.authorized.init', response);
989
+ // Authorised on the paired device; the token is about to be submitted.
990
+ SallaApplePay.trackPaymentSubmitted();
889
991
 
890
992
  let authorizedData;
891
993
  try {
@@ -896,10 +998,16 @@ const SallaApplePay = typeof window !== 'undefined' ? {
896
998
 
897
999
  authorizedData = data;
898
1000
  Salla.event.dispatch('payments::apple-pay.authorized.success', data);
1001
+ SallaApplePay.trackPaymentSucceeded(data);
899
1002
  await response.complete('success');
900
1003
  } catch (error) {
901
1004
  const errorResponse = error?.response;
902
1005
  Salla.event.dispatch('payments::apple-pay.authorized.failed', errorResponse);
1006
+ // Backend rejected the token — latch set so the sheet-close /
1007
+ // cancel that follows can't emit a second Failed.
1008
+ SallaApplePay.trackPaymentFailed('server',
1009
+ errorResponse?.data?.error?.code || 'APPLE_PAY_FAILED',
1010
+ errorResponse?.data?.error?.message || '');
903
1011
  await response.complete('fail');
904
1012
  if (typeof SallaApplePay.detail.authorized.onFailed === 'function') {
905
1013
  SallaApplePay.detail.authorized.onFailed(errorResponse);