@flopay/vue 1.13.5 → 1.14.0

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 CHANGED
@@ -73,10 +73,10 @@ Every `@flopay/react` component and hook has a same-name Vue export. React's not
73
73
  | Export | React (`@flopay/react`) | Vue (`@flopay/vue`) |
74
74
  |--------|-------------------------|---------------------|
75
75
  | `FloPayProvider` | `<FloPayProvider>` | `FloPayProvider`, provided to descendants; `@instrument` |
76
- | `FloPayCheckout` | `<FloPayCheckout>` | `FloPayCheckout`: `@complete`, `@decline`, `@error`, `@button-click` |
77
- | `SplitCardForm` | `<SplitCardForm>` | `SplitCardForm`: `@complete`, `@decline`, `@error` |
76
+ | `FloPayCheckout` | `<FloPayCheckout>` | `FloPayCheckout`: `@complete`, `@decline`, `@cancel`, `@error`, `@button-click` |
77
+ | `SplitCardForm` | `<SplitCardForm>` | `SplitCardForm`: `@complete`, `@decline`, `@cancel`, `@error` |
78
78
  | `VaultCardFields` | `<VaultCardFields>` | `VaultCardFields`: `@ready`, `@error`, `@validation` |
79
- | `DirectPayPalButton` | `<DirectPayPalButton>` | `DirectPayPalButton` |
79
+ | `DirectPayPalButton` | `<DirectPayPalButton>` | `DirectPayPalButton`: `@complete`, `@decline`, `@cancel`, `@error` |
80
80
  | `FloPayAutomaticPaymentButton` | `<FloPayAutomaticPaymentButton>` | `FloPayAutomaticPaymentButton`: `@success`, `@error`, `@decline` |
81
81
  | `FloPayCardSetup` | `<FloPayCardSetup>` | `FloPayCardSetup`: `@ready`, `@complete`, `@decline`, `@cancel`, `@error` |
82
82
  | `FloPayPaymentMethodSetup` | `<FloPayPaymentMethodSetup>` | `FloPayPaymentMethodSetup` |
@@ -90,6 +90,47 @@ React `children` and custom layouts are slots. `FloPayCheckout`'s `layout` slot
90
90
 
91
91
  Entry points: `@flopay/vue`, `@flopay/vue/card-setup` and `@flopay/vue/payment-method-setup`.
92
92
 
93
+ ### PayPal buyer cancel
94
+
95
+ `FloPayCheckout`, `SplitCardForm` and `DirectPayPalButton` emit `cancel` (`@cancel`, React's `onCancel`) once per click when the buyer closes the PayPal pop-up, or returns from PayPal's app switch, without approving. The payload is a `FloPayPaymentCancelEvent`: `{ status: 'cancelled', sessionId, provider: 'paypal', paymentMethodType }`. `sessionId` is the attempt's session (the click-time patch's, otherwise the checkout's), and `paymentMethodType` is `paypal_vaulted` when the session advertises an `order` or `setup_token`, otherwise `paypal`.
96
+
97
+ It is never emitted for an approval, a decline (that stays `@decline`), a blocked pop-up, a load, render or eligibility failure, or a click your `runBeforeButtonClick` gate stopped, and a repeated cancel for the same click is ignored. It covers the direct PayPal button only (sessions with `gateways.paypal`). The button stays rendered for the next click, and nothing changes if you don't listen for it.
98
+
99
+ A "Didn't work? Pay another way" prompt that moves the buyer to the card form:
100
+
101
+ ```ts
102
+ import { FloPayCheckout, type FloPayPaymentCancelEvent } from '@flopay/vue';
103
+ import { defineComponent, h, ref } from 'vue';
104
+
105
+ export default defineComponent({
106
+ setup() {
107
+ const cancelled = ref<FloPayPaymentCancelEvent | null>(null);
108
+ const cardForm = ref<HTMLElement | null>(null);
109
+ return () => [
110
+ cancelled.value
111
+ ? h('div', { role: 'status' }, [
112
+ "Didn't work? Pay another way. ",
113
+ h(
114
+ 'button',
115
+ { type: 'button', onClick: () => cardForm.value?.focus() },
116
+ 'Pay by card or Google Pay',
117
+ ),
118
+ ])
119
+ : null,
120
+ h('div', { ref: cardForm, tabindex: -1 }, [
121
+ h(FloPayCheckout, {
122
+ sessionId: 'sess_123',
123
+ nonce: 'nonce_123',
124
+ onCancel: (event: FloPayPaymentCancelEvent) => {
125
+ cancelled.value = event;
126
+ },
127
+ }),
128
+ ]),
129
+ ];
130
+ },
131
+ });
132
+ ```
133
+
93
134
  ## Errors
94
135
 
95
136
  Errors are `FloPayError` instances from `@flopay/shared`, emitted through `@error`.
package/dist/index.cjs CHANGED
@@ -1375,6 +1375,8 @@ var DirectPayPalButton = (0, import_vue3.defineComponent)({
1375
1375
  emits: {
1376
1376
  complete: (_result) => true,
1377
1377
  decline: (_decline) => true,
1378
+ /** The buyer closed or left PayPal without approving (once per click attempt). */
1379
+ cancel: (_event) => true,
1378
1380
  error: (_error) => true,
1379
1381
  errorChange: (_message) => true,
1380
1382
  buttonClick: (_method) => true,
@@ -1393,6 +1395,7 @@ var DirectPayPalButton = (0, import_vue3.defineComponent)({
1393
1395
  let generation = 0;
1394
1396
  let retryTimer = null;
1395
1397
  let prepared = null;
1398
+ let attemptLive = false;
1396
1399
  let intentIdempotencyKey;
1397
1400
  let intentAttemptActive = false;
1398
1401
  const beginIntentAttempt = () => {
@@ -1519,6 +1522,7 @@ var DirectPayPalButton = (0, import_vue3.defineComponent)({
1519
1522
  (0, import_shared4.invokeMerchantCallback)(() => emit("errorChange", recoveryMessage));
1520
1523
  };
1521
1524
  const runtimeFailure = (error) => {
1525
+ attemptLive = false;
1522
1526
  const message = errorMessage(error, "PayPal failed to render.");
1523
1527
  if ((0, import_shared4.isPayPalTeardownMessage)(message)) {
1524
1528
  prepared = null;
@@ -1614,6 +1618,7 @@ var DirectPayPalButton = (0, import_vue3.defineComponent)({
1614
1618
  };
1615
1619
  const approve = async (gen, data, actions) => {
1616
1620
  if (!isCurrent(gen)) return;
1621
+ attemptLive = false;
1617
1622
  const attempt = { ...prepared ?? {} };
1618
1623
  submitting.value = true;
1619
1624
  trace("onApprove");
@@ -1660,6 +1665,7 @@ var DirectPayPalButton = (0, import_vue3.defineComponent)({
1660
1665
  }
1661
1666
  beginIntentAttempt();
1662
1667
  prepared = null;
1668
+ attemptLive = false;
1663
1669
  let next = {};
1664
1670
  const runner = props.runBeforeButtonClick;
1665
1671
  if (runner) {
@@ -1689,9 +1695,18 @@ var DirectPayPalButton = (0, import_vue3.defineComponent)({
1689
1695
  }
1690
1696
  (0, import_shared4.invokeMerchantCallback)(() => emit("buttonClick", "paypal"));
1691
1697
  prepared = next;
1698
+ attemptLive = true;
1692
1699
  trace("onClick:resolve");
1693
1700
  await actions.resolve();
1694
1701
  };
1702
+ const cancel = (gen) => {
1703
+ if (!isCurrent(gen) || !attemptLive) return;
1704
+ attemptLive = false;
1705
+ const event = (0, import_shared4.buildPayPalCancelEvent)(effectiveSessionId(), configuredObjectType());
1706
+ prepared = null;
1707
+ trace("onCancel");
1708
+ (0, import_shared4.invokeMerchantCallback)(() => emit("cancel", event));
1709
+ };
1695
1710
  const renderButtons = async (gen) => {
1696
1711
  const normalized = (0, import_shared4.normalizeGatewayEnvironment)(props.environment);
1697
1712
  const environment = normalized === "live" ? "production" : normalized;
@@ -1743,6 +1758,7 @@ var DirectPayPalButton = (0, import_vue3.defineComponent)({
1743
1758
  ...handlers,
1744
1759
  onClick: (_data, actions) => click(gen, actions),
1745
1760
  onApprove: (data, actions) => approve(gen, data, actions),
1761
+ onCancel: () => cancel(gen),
1746
1762
  onError: (error) => {
1747
1763
  if (!isCurrent(gen)) return;
1748
1764
  trace(
@@ -1827,6 +1843,7 @@ var DirectPayPalButton = (0, import_vue3.defineComponent)({
1827
1843
  if (retryTimer) clearTimeout(retryTimer);
1828
1844
  retryTimer = null;
1829
1845
  prepared = null;
1846
+ attemptLive = false;
1830
1847
  intentIdempotencyKey = void 0;
1831
1848
  intentAttemptActive = false;
1832
1849
  closeButtons();
@@ -2192,6 +2209,8 @@ var SplitCardForm = (0, import_vue6.defineComponent)({
2192
2209
  complete: (_result) => true,
2193
2210
  error: (_error) => true,
2194
2211
  decline: (_decline) => true,
2212
+ // React's `onCancel`: the direct PayPal button's buyer cancel, re-emitted once.
2213
+ cancel: (_event) => true,
2195
2214
  buttonClick: (_method) => true,
2196
2215
  countryChange: (_country) => true,
2197
2216
  // React's `onZipChange`.
@@ -3200,6 +3219,7 @@ var SplitCardForm = (0, import_vue6.defineComponent)({
3200
3219
  // React's `onTokenizedBody={dispatchTokenizedBody}`: the form
3201
3220
  // continues the approval (or hands it to the host's handler).
3202
3221
  onTokenizedBody: dispatchTokenizedBody,
3222
+ onCancel: (event) => (0, import_shared8.invokeMerchantCallback)(() => emit("cancel", event)),
3203
3223
  onButtonClick: (method) => (0, import_shared8.invokeMerchantCallback)(() => emit("buttonClick", method)),
3204
3224
  onErrorChange: (message) => {
3205
3225
  failure.value = message;
@@ -3494,6 +3514,8 @@ var FloPayCheckout = (0, import_vue7.defineComponent)({
3494
3514
  error: (_error) => true,
3495
3515
  instrument: (_event) => true,
3496
3516
  decline: (_decline) => true,
3517
+ // React's `onCancel`: a direct PayPal buyer cancel from the built-in form.
3518
+ cancel: (_event) => true,
3497
3519
  buttonClick: (_method) => true,
3498
3520
  countryChange: (_country) => true,
3499
3521
  // React's `onZipChange`.
@@ -3886,6 +3908,9 @@ var FloPayCheckout = (0, import_vue7.defineComponent)({
3886
3908
  enabledPaymentMethodCountries: stripeCountryGate.value,
3887
3909
  debug: props.debug,
3888
3910
  ...relay,
3911
+ // React's `onCancel={onCancel}`: the built-in form's direct PayPal
3912
+ // cancel reaches the merchant once.
3913
+ onCancel: (event) => (0, import_shared9.invokeMerchantCallback)(() => emit("cancel", event)),
3889
3914
  onCountryChange: (country) => (0, import_shared9.invokeMerchantCallback)(() => emit("countryChange", country)),
3890
3915
  onZipChange: (zip) => (0, import_shared9.invokeMerchantCallback)(() => emit("zipChange", zip))
3891
3916
  },
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as _flopay_shared from '@flopay/shared';
2
- import { InlineSessionDraft, CheckoutButtonMethod, CheckoutProduct, CheckoutItem, CheckoutSubscription, ThemeId, FloPayAppearance, ButtonsLayoutTheme, ButtonsLayoutStyles, PaymentResult, CheckoutSession, FloPayError, DeclineEvent, PayPalBuyerIdentity, GatewayEnvironment, PayPalProviderObjectType, PayPalApprovalPresentation, InlineSessionPatch, TokenizedBody, TelemetryTerminalReason, BeforeButtonClickEvent, WalletPresentation, AVSFieldConfig, VaultCardFieldKey, CheckoutMode, FloInstrumentEvent, CardCaptureAdapter, VaultCardThemeColors } from '@flopay/shared';
3
- export { BeforeButtonClickEvent, CheckoutButtonMethod, DeclineEvent, FloInstrumentEvent, InlineSessionDraft, InlineSessionPatch, SentryEventLike, SentryStackFrameLike, WalletPresentation, dropThirdPartyOnlyError } from '@flopay/shared';
2
+ import { InlineSessionDraft, CheckoutButtonMethod, CheckoutProduct, CheckoutItem, CheckoutSubscription, ThemeId, FloPayAppearance, ButtonsLayoutTheme, ButtonsLayoutStyles, PaymentResult, CheckoutSession, FloPayError, DeclineEvent, PayPalBuyerIdentity, GatewayEnvironment, PayPalProviderObjectType, PayPalApprovalPresentation, InlineSessionPatch, TokenizedBody, TelemetryTerminalReason, FloPayPaymentCancelEvent, BeforeButtonClickEvent, WalletPresentation, AVSFieldConfig, VaultCardFieldKey, CheckoutMode, FloInstrumentEvent, CardCaptureAdapter, VaultCardThemeColors } from '@flopay/shared';
3
+ export { BeforeButtonClickEvent, CheckoutButtonMethod, DeclineEvent, FloInstrumentEvent, FloPayPaymentCancelEvent, InlineSessionDraft, InlineSessionPatch, SentryEventLike, SentryStackFrameLike, WalletPresentation, dropThirdPartyOnlyError } from '@flopay/shared';
4
4
  import * as vue from 'vue';
5
5
  import { PropType, VNode, ShallowRef, CSSProperties } from 'vue';
6
6
  export { FloPayCardSetup, FloPayCardSetupProps } from './card-setup-entry.cjs';
@@ -480,6 +480,8 @@ declare const DirectPayPalButton: vue.DefineComponent<vue.ExtractPropTypes<{
480
480
  }> | null, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {
481
481
  complete: (_result: PaymentResult) => true;
482
482
  decline: (_decline: DeclineEvent) => true;
483
+ /** The buyer closed or left PayPal without approving (once per click attempt). */
484
+ cancel: (_event: FloPayPaymentCancelEvent) => true;
483
485
  error: (_error: FloPayError) => true;
484
486
  errorChange: (_message: string | null) => true;
485
487
  buttonClick: (_method: CheckoutButtonMethod) => true;
@@ -572,6 +574,7 @@ declare const DirectPayPalButton: vue.DefineComponent<vue.ExtractPropTypes<{
572
574
  };
573
575
  }>> & Readonly<{
574
576
  onComplete?: ((_result: PaymentResult) => any) | undefined;
577
+ onCancel?: ((_event: FloPayPaymentCancelEvent) => any) | undefined;
575
578
  onError?: ((_error: FloPayError) => any) | undefined;
576
579
  onDecline?: ((_decline: DeclineEvent) => any) | undefined;
577
580
  onErrorChange?: ((_message: string | null) => any) | undefined;
@@ -1015,6 +1018,7 @@ declare const SplitCardForm: vue.DefineComponent<vue.ExtractPropTypes<{
1015
1018
  complete: (_result: PaymentResult) => true;
1016
1019
  error: (_error: FloPayError) => true;
1017
1020
  decline: (_decline: DeclineEvent) => true;
1021
+ cancel: (_event: FloPayPaymentCancelEvent) => true;
1018
1022
  buttonClick: (_method: CheckoutButtonMethod) => true;
1019
1023
  countryChange: (_country: string) => true;
1020
1024
  zipChange: (_zip: string) => true;
@@ -1229,6 +1233,7 @@ declare const SplitCardForm: vue.DefineComponent<vue.ExtractPropTypes<{
1229
1233
  };
1230
1234
  }>> & Readonly<{
1231
1235
  onComplete?: ((_result: PaymentResult) => any) | undefined;
1236
+ onCancel?: ((_event: FloPayPaymentCancelEvent) => any) | undefined;
1232
1237
  onError?: ((_error: FloPayError) => any) | undefined;
1233
1238
  onDecline?: ((_decline: DeclineEvent) => any) | undefined;
1234
1239
  onErrorChange?: ((_error: string | null) => any) | undefined;
@@ -1537,6 +1542,7 @@ declare const FloPayCheckout: vue.DefineComponent<vue.ExtractPropTypes<{
1537
1542
  error: (_error: FloPayError) => true;
1538
1543
  instrument: (_event: FloInstrumentEvent) => true;
1539
1544
  decline: (_decline: DeclineEvent) => true;
1545
+ cancel: (_event: FloPayPaymentCancelEvent) => true;
1540
1546
  buttonClick: (_method: CheckoutButtonMethod) => true;
1541
1547
  countryChange: (_country: string) => true;
1542
1548
  zipChange: (_zip: string) => true;
@@ -1679,6 +1685,7 @@ declare const FloPayCheckout: vue.DefineComponent<vue.ExtractPropTypes<{
1679
1685
  }>> & Readonly<{
1680
1686
  onInstrument?: ((_event: FloInstrumentEvent) => any) | undefined;
1681
1687
  onComplete?: ((_result: PaymentResult) => any) | undefined;
1688
+ onCancel?: ((_event: FloPayPaymentCancelEvent) => any) | undefined;
1682
1689
  onError?: ((_error: FloPayError) => any) | undefined;
1683
1690
  onDecline?: ((_decline: DeclineEvent) => any) | undefined;
1684
1691
  onButtonClick?: ((_method: CheckoutButtonMethod) => any) | undefined;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as _flopay_shared from '@flopay/shared';
2
- import { InlineSessionDraft, CheckoutButtonMethod, CheckoutProduct, CheckoutItem, CheckoutSubscription, ThemeId, FloPayAppearance, ButtonsLayoutTheme, ButtonsLayoutStyles, PaymentResult, CheckoutSession, FloPayError, DeclineEvent, PayPalBuyerIdentity, GatewayEnvironment, PayPalProviderObjectType, PayPalApprovalPresentation, InlineSessionPatch, TokenizedBody, TelemetryTerminalReason, BeforeButtonClickEvent, WalletPresentation, AVSFieldConfig, VaultCardFieldKey, CheckoutMode, FloInstrumentEvent, CardCaptureAdapter, VaultCardThemeColors } from '@flopay/shared';
3
- export { BeforeButtonClickEvent, CheckoutButtonMethod, DeclineEvent, FloInstrumentEvent, InlineSessionDraft, InlineSessionPatch, SentryEventLike, SentryStackFrameLike, WalletPresentation, dropThirdPartyOnlyError } from '@flopay/shared';
2
+ import { InlineSessionDraft, CheckoutButtonMethod, CheckoutProduct, CheckoutItem, CheckoutSubscription, ThemeId, FloPayAppearance, ButtonsLayoutTheme, ButtonsLayoutStyles, PaymentResult, CheckoutSession, FloPayError, DeclineEvent, PayPalBuyerIdentity, GatewayEnvironment, PayPalProviderObjectType, PayPalApprovalPresentation, InlineSessionPatch, TokenizedBody, TelemetryTerminalReason, FloPayPaymentCancelEvent, BeforeButtonClickEvent, WalletPresentation, AVSFieldConfig, VaultCardFieldKey, CheckoutMode, FloInstrumentEvent, CardCaptureAdapter, VaultCardThemeColors } from '@flopay/shared';
3
+ export { BeforeButtonClickEvent, CheckoutButtonMethod, DeclineEvent, FloInstrumentEvent, FloPayPaymentCancelEvent, InlineSessionDraft, InlineSessionPatch, SentryEventLike, SentryStackFrameLike, WalletPresentation, dropThirdPartyOnlyError } from '@flopay/shared';
4
4
  import * as vue from 'vue';
5
5
  import { PropType, VNode, ShallowRef, CSSProperties } from 'vue';
6
6
  export { FloPayCardSetup, FloPayCardSetupProps } from './card-setup-entry.js';
@@ -480,6 +480,8 @@ declare const DirectPayPalButton: vue.DefineComponent<vue.ExtractPropTypes<{
480
480
  }> | null, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {
481
481
  complete: (_result: PaymentResult) => true;
482
482
  decline: (_decline: DeclineEvent) => true;
483
+ /** The buyer closed or left PayPal without approving (once per click attempt). */
484
+ cancel: (_event: FloPayPaymentCancelEvent) => true;
483
485
  error: (_error: FloPayError) => true;
484
486
  errorChange: (_message: string | null) => true;
485
487
  buttonClick: (_method: CheckoutButtonMethod) => true;
@@ -572,6 +574,7 @@ declare const DirectPayPalButton: vue.DefineComponent<vue.ExtractPropTypes<{
572
574
  };
573
575
  }>> & Readonly<{
574
576
  onComplete?: ((_result: PaymentResult) => any) | undefined;
577
+ onCancel?: ((_event: FloPayPaymentCancelEvent) => any) | undefined;
575
578
  onError?: ((_error: FloPayError) => any) | undefined;
576
579
  onDecline?: ((_decline: DeclineEvent) => any) | undefined;
577
580
  onErrorChange?: ((_message: string | null) => any) | undefined;
@@ -1015,6 +1018,7 @@ declare const SplitCardForm: vue.DefineComponent<vue.ExtractPropTypes<{
1015
1018
  complete: (_result: PaymentResult) => true;
1016
1019
  error: (_error: FloPayError) => true;
1017
1020
  decline: (_decline: DeclineEvent) => true;
1021
+ cancel: (_event: FloPayPaymentCancelEvent) => true;
1018
1022
  buttonClick: (_method: CheckoutButtonMethod) => true;
1019
1023
  countryChange: (_country: string) => true;
1020
1024
  zipChange: (_zip: string) => true;
@@ -1229,6 +1233,7 @@ declare const SplitCardForm: vue.DefineComponent<vue.ExtractPropTypes<{
1229
1233
  };
1230
1234
  }>> & Readonly<{
1231
1235
  onComplete?: ((_result: PaymentResult) => any) | undefined;
1236
+ onCancel?: ((_event: FloPayPaymentCancelEvent) => any) | undefined;
1232
1237
  onError?: ((_error: FloPayError) => any) | undefined;
1233
1238
  onDecline?: ((_decline: DeclineEvent) => any) | undefined;
1234
1239
  onErrorChange?: ((_error: string | null) => any) | undefined;
@@ -1537,6 +1542,7 @@ declare const FloPayCheckout: vue.DefineComponent<vue.ExtractPropTypes<{
1537
1542
  error: (_error: FloPayError) => true;
1538
1543
  instrument: (_event: FloInstrumentEvent) => true;
1539
1544
  decline: (_decline: DeclineEvent) => true;
1545
+ cancel: (_event: FloPayPaymentCancelEvent) => true;
1540
1546
  buttonClick: (_method: CheckoutButtonMethod) => true;
1541
1547
  countryChange: (_country: string) => true;
1542
1548
  zipChange: (_zip: string) => true;
@@ -1679,6 +1685,7 @@ declare const FloPayCheckout: vue.DefineComponent<vue.ExtractPropTypes<{
1679
1685
  }>> & Readonly<{
1680
1686
  onInstrument?: ((_event: FloInstrumentEvent) => any) | undefined;
1681
1687
  onComplete?: ((_result: PaymentResult) => any) | undefined;
1688
+ onCancel?: ((_event: FloPayPaymentCancelEvent) => any) | undefined;
1682
1689
  onError?: ((_error: FloPayError) => any) | undefined;
1683
1690
  onDecline?: ((_decline: DeclineEvent) => any) | undefined;
1684
1691
  onButtonClick?: ((_method: CheckoutButtonMethod) => any) | undefined;
package/dist/index.mjs CHANGED
@@ -256,6 +256,7 @@ import { PaymentAPI } from "@flopay/js/card-setup";
256
256
  import {
257
257
  applyFriendlyMessageOverride,
258
258
  buildExternalMethodRecoveryMessage,
259
+ buildPayPalCancelEvent,
259
260
  buildPayPalProcessPaymentRequest,
260
261
  buildPayPalSessionIntentRequest,
261
262
  buildPayPalTokenizedBody,
@@ -475,6 +476,8 @@ var DirectPayPalButton = defineComponent2({
475
476
  emits: {
476
477
  complete: (_result) => true,
477
478
  decline: (_decline) => true,
479
+ /** The buyer closed or left PayPal without approving (once per click attempt). */
480
+ cancel: (_event) => true,
478
481
  error: (_error) => true,
479
482
  errorChange: (_message) => true,
480
483
  buttonClick: (_method) => true,
@@ -493,6 +496,7 @@ var DirectPayPalButton = defineComponent2({
493
496
  let generation = 0;
494
497
  let retryTimer = null;
495
498
  let prepared = null;
499
+ let attemptLive = false;
496
500
  let intentIdempotencyKey;
497
501
  let intentAttemptActive = false;
498
502
  const beginIntentAttempt = () => {
@@ -619,6 +623,7 @@ var DirectPayPalButton = defineComponent2({
619
623
  invokeMerchantCallback(() => emit("errorChange", recoveryMessage));
620
624
  };
621
625
  const runtimeFailure = (error) => {
626
+ attemptLive = false;
622
627
  const message = errorMessage(error, "PayPal failed to render.");
623
628
  if (isPayPalTeardownMessage(message)) {
624
629
  prepared = null;
@@ -714,6 +719,7 @@ var DirectPayPalButton = defineComponent2({
714
719
  };
715
720
  const approve = async (gen, data, actions) => {
716
721
  if (!isCurrent(gen)) return;
722
+ attemptLive = false;
717
723
  const attempt = { ...prepared ?? {} };
718
724
  submitting.value = true;
719
725
  trace("onApprove");
@@ -760,6 +766,7 @@ var DirectPayPalButton = defineComponent2({
760
766
  }
761
767
  beginIntentAttempt();
762
768
  prepared = null;
769
+ attemptLive = false;
763
770
  let next = {};
764
771
  const runner = props.runBeforeButtonClick;
765
772
  if (runner) {
@@ -789,9 +796,18 @@ var DirectPayPalButton = defineComponent2({
789
796
  }
790
797
  invokeMerchantCallback(() => emit("buttonClick", "paypal"));
791
798
  prepared = next;
799
+ attemptLive = true;
792
800
  trace("onClick:resolve");
793
801
  await actions.resolve();
794
802
  };
803
+ const cancel = (gen) => {
804
+ if (!isCurrent(gen) || !attemptLive) return;
805
+ attemptLive = false;
806
+ const event = buildPayPalCancelEvent(effectiveSessionId(), configuredObjectType());
807
+ prepared = null;
808
+ trace("onCancel");
809
+ invokeMerchantCallback(() => emit("cancel", event));
810
+ };
795
811
  const renderButtons = async (gen) => {
796
812
  const normalized = normalizeGatewayEnvironment(props.environment);
797
813
  const environment = normalized === "live" ? "production" : normalized;
@@ -843,6 +859,7 @@ var DirectPayPalButton = defineComponent2({
843
859
  ...handlers,
844
860
  onClick: (_data, actions) => click(gen, actions),
845
861
  onApprove: (data, actions) => approve(gen, data, actions),
862
+ onCancel: () => cancel(gen),
846
863
  onError: (error) => {
847
864
  if (!isCurrent(gen)) return;
848
865
  trace(
@@ -927,6 +944,7 @@ var DirectPayPalButton = defineComponent2({
927
944
  if (retryTimer) clearTimeout(retryTimer);
928
945
  retryTimer = null;
929
946
  prepared = null;
947
+ attemptLive = false;
930
948
  intentIdempotencyKey = void 0;
931
949
  intentAttemptActive = false;
932
950
  closeButtons();
@@ -1173,6 +1191,8 @@ var SplitCardForm = defineComponent3({
1173
1191
  complete: (_result) => true,
1174
1192
  error: (_error) => true,
1175
1193
  decline: (_decline) => true,
1194
+ // React's `onCancel`: the direct PayPal button's buyer cancel, re-emitted once.
1195
+ cancel: (_event) => true,
1176
1196
  buttonClick: (_method) => true,
1177
1197
  countryChange: (_country) => true,
1178
1198
  // React's `onZipChange`.
@@ -2181,6 +2201,7 @@ var SplitCardForm = defineComponent3({
2181
2201
  // React's `onTokenizedBody={dispatchTokenizedBody}`: the form
2182
2202
  // continues the approval (or hands it to the host's handler).
2183
2203
  onTokenizedBody: dispatchTokenizedBody,
2204
+ onCancel: (event) => invokeMerchantCallback2(() => emit("cancel", event)),
2184
2205
  onButtonClick: (method) => invokeMerchantCallback2(() => emit("buttonClick", method)),
2185
2206
  onErrorChange: (message) => {
2186
2207
  failure.value = message;
@@ -2475,6 +2496,8 @@ var FloPayCheckout = defineComponent4({
2475
2496
  error: (_error) => true,
2476
2497
  instrument: (_event) => true,
2477
2498
  decline: (_decline) => true,
2499
+ // React's `onCancel`: a direct PayPal buyer cancel from the built-in form.
2500
+ cancel: (_event) => true,
2478
2501
  buttonClick: (_method) => true,
2479
2502
  countryChange: (_country) => true,
2480
2503
  // React's `onZipChange`.
@@ -2867,6 +2890,9 @@ var FloPayCheckout = defineComponent4({
2867
2890
  enabledPaymentMethodCountries: stripeCountryGate.value,
2868
2891
  debug: props.debug,
2869
2892
  ...relay,
2893
+ // React's `onCancel={onCancel}`: the built-in form's direct PayPal
2894
+ // cancel reaches the merchant once.
2895
+ onCancel: (event) => invokeMerchantCallback3(() => emit("cancel", event)),
2870
2896
  onCountryChange: (country) => invokeMerchantCallback3(() => emit("countryChange", country)),
2871
2897
  onZipChange: (zip) => invokeMerchantCallback3(() => emit("zipChange", zip))
2872
2898
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flopay/vue",
3
- "version": "1.13.5",
3
+ "version": "1.14.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "publishConfig": {
@@ -36,8 +36,8 @@
36
36
  "dist"
37
37
  ],
38
38
  "dependencies": {
39
- "@flopay/js": "1.13.5",
40
- "@flopay/shared": "1.13.5",
39
+ "@flopay/js": "1.14.0",
40
+ "@flopay/shared": "1.14.0",
41
41
  "@paypal/paypal-js": "^10.1.0",
42
42
  "@stripe/stripe-js": "^9.17.0"
43
43
  },