From c09a9e388f19bfea8f2ae6844bdf0b2113e85724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jasi=C5=84ski?= Date: Wed, 5 Aug 2026 09:10:07 +0200 Subject: [PATCH 1/2] fix: fix customer cancelled typo --- src/dynamic-checkout/payment-methods/apm.ts | 4 ++-- src/dynamic-checkout/payment-methods/card.ts | 2 +- .../payment-methods/saved-apm.ts | 4 ++-- .../payment-methods/saved-card.ts | 2 +- src/dynamic-checkout/references.ts | 1 + src/dynamic-checkout/utils/errors.ts | 11 ++++++++++ test/dynamic-checkout/errors.test.ts | 20 +++++++++++++++++++ test/support/loadNamespace.ts | 13 ++++++++++++ 8 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 src/dynamic-checkout/utils/errors.ts create mode 100644 test/dynamic-checkout/errors.test.ts diff --git a/src/dynamic-checkout/payment-methods/apm.ts b/src/dynamic-checkout/payment-methods/apm.ts index 1c8bdaab..c83a6956 100644 --- a/src/dynamic-checkout/payment-methods/apm.ts +++ b/src/dynamic-checkout/payment-methods/apm.ts @@ -176,7 +176,7 @@ module ProcessOut { ) }, error => { - if (error.code === "customer.canceled") { + if (isCustomerCancellationError(error)) { this.resetContainerHtml().appendChild( new DynamicCheckoutPaymentCancelledView(this.processOutInstance, this.paymentConfig) .element, @@ -364,7 +364,7 @@ module ProcessOut { ) }, error => { - if (error.code === "customer.canceled") { + if (isCustomerCancellationError(error)) { this.resetContainerHtml().appendChild( new DynamicCheckoutPaymentCancelledView( this.processOutInstance, diff --git a/src/dynamic-checkout/payment-methods/card.ts b/src/dynamic-checkout/payment-methods/card.ts index 7dfb18cc..9e6405cd 100644 --- a/src/dynamic-checkout/payment-methods/card.ts +++ b/src/dynamic-checkout/payment-methods/card.ts @@ -220,7 +220,7 @@ module ProcessOut { } private handleCardPaymentError(error) { - if (error.code === "customer.canceled") { + if (isCustomerCancellationError(error)) { this.resetContainerHtml().appendChild( new DynamicCheckoutPaymentCancelledView(this.processOutInstance, this.paymentConfig) .element, diff --git a/src/dynamic-checkout/payment-methods/saved-apm.ts b/src/dynamic-checkout/payment-methods/saved-apm.ts index efb0998d..3a25ec2e 100644 --- a/src/dynamic-checkout/payment-methods/saved-apm.ts +++ b/src/dynamic-checkout/payment-methods/saved-apm.ts @@ -161,7 +161,7 @@ module ProcessOut { ) }, error => { - if (error.code === "customer.canceled") { + if (isCustomerCancellationError(error)) { this.resetContainerHtml().appendChild( new DynamicCheckoutPaymentCancelledView( this.processOutInstance, @@ -255,7 +255,7 @@ module ProcessOut { } private handlePaymentError(error) { - if (error.code === "customer.canceled") { + if (isCustomerCancellationError(error)) { this.resetContainerHtml().appendChild( new DynamicCheckoutPaymentCancelledView(this.processOutInstance, this.paymentConfig) .element, diff --git a/src/dynamic-checkout/payment-methods/saved-card.ts b/src/dynamic-checkout/payment-methods/saved-card.ts index 5a4c2391..efef5047 100644 --- a/src/dynamic-checkout/payment-methods/saved-card.ts +++ b/src/dynamic-checkout/payment-methods/saved-card.ts @@ -107,7 +107,7 @@ module ProcessOut { } private handlePaymentError(error) { - if (error.code === "customer.canceled") { + if (isCustomerCancellationError(error)) { this.resetContainerHtml().appendChild( new DynamicCheckoutPaymentCancelledView(this.processOutInstance, this.paymentConfig) .element, diff --git a/src/dynamic-checkout/references.ts b/src/dynamic-checkout/references.ts index 24f1b846..af2174e6 100644 --- a/src/dynamic-checkout/references.ts +++ b/src/dynamic-checkout/references.ts @@ -38,6 +38,7 @@ /// /// /// +/// /// /// /// diff --git a/src/dynamic-checkout/utils/errors.ts b/src/dynamic-checkout/utils/errors.ts new file mode 100644 index 00000000..85934ac7 --- /dev/null +++ b/src/dynamic-checkout/utils/errors.ts @@ -0,0 +1,11 @@ +/// + +module ProcessOut { + // The SDK raises "customer.canceled" for cancellations it detects itself + // (overlay cancel, tab/window closed), while cancellations that happen on a + // gateway's hosted page come back from the API as "customer.cancelled". + // Both must be treated as a customer cancellation. + export function isCustomerCancellationError(error: { code?: string }): boolean { + return error.code === "customer.canceled" || error.code === "customer.cancelled" + } +} diff --git a/test/dynamic-checkout/errors.test.ts b/test/dynamic-checkout/errors.test.ts new file mode 100644 index 00000000..3121ac23 --- /dev/null +++ b/test/dynamic-checkout/errors.test.ts @@ -0,0 +1,20 @@ +import { describe, expect, it } from "vitest" +import { loadNamespaceFile } from "../support/loadNamespace" + +const { isCustomerCancellationError } = loadNamespaceFile("src/dynamic-checkout/utils/errors.ts") + +describe("isCustomerCancellationError", () => { + it("matches the SDK-internal cancellation code", () => { + expect(isCustomerCancellationError({ code: "customer.canceled" })).toBe(true) + }) + + it("matches the API cancellation code raised by gateway-hosted pages", () => { + expect(isCustomerCancellationError({ code: "customer.cancelled" })).toBe(true) + }) + + it("rejects other error codes", () => { + expect(isCustomerCancellationError({ code: "customer.popup-blocked" })).toBe(false) + expect(isCustomerCancellationError({ code: "card.declined" })).toBe(false) + expect(isCustomerCancellationError({})).toBe(false) + }) +}) diff --git a/test/support/loadNamespace.ts b/test/support/loadNamespace.ts index 546c1ce5..d9989565 100644 --- a/test/support/loadNamespace.ts +++ b/test/support/loadNamespace.ts @@ -56,3 +56,16 @@ export function loadApmUtils(navigator: FakeNavigator = {}): Record run(moduleShim, moduleShim.exports, navigator) return moduleShim.exports } + +/** + * Load a self-contained namespace source file and return its `ProcessOut` + * namespace. Only suitable for files with no dependencies on the rest of + * the bundle or on browser globals. + */ +export function loadNamespaceFile(relativePath: string): Record { + const js = compile(relativePath) + const moduleShim = { exports: {} as Record } + const run = new Function("module", "exports", `${js}\nmodule.exports = ProcessOut;`) + run(moduleShim, moduleShim.exports) + return moduleShim.exports +} From 3b8a8f731cc67009b0574b0f1b25dadfe111c502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jasi=C5=84ski?= Date: Wed, 5 Aug 2026 09:10:28 +0200 Subject: [PATCH 2/2] bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4b6112af..faa35f1b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "processout.js", - "version": "1.9.10", + "version": "1.9.11", "description": "ProcessOut.js is a JavaScript library for ProcessOut's payment processing API.", "scripts": { "build:processout": "tsc -p src/processout && uglifyjs --compress --keep-fnames --ie8 dist/processout.js -o dist/processout.js",