diff --git a/src/components/CheckoutDemo/CheckoutDemoInner.tsx b/src/components/CheckoutDemo/CheckoutDemoInner.tsx index ce5adaab..8c6cbbd8 100644 --- a/src/components/CheckoutDemo/CheckoutDemoInner.tsx +++ b/src/components/CheckoutDemo/CheckoutDemoInner.tsx @@ -5,11 +5,16 @@ import { createSandboxSession, callPaymentMethods, extractPgCodes, + type PaymentPlugin, } from "@site/src/utils/sandbox"; import { createDemoCallbacks } from "@site/src/utils/checkoutSdk"; import CheckoutSDKEmbed from "@site/src/components/CheckoutSDKEmbed"; import styles from "./styles.module.css"; +// Single-sourced: gateway discovery and session creation must name the same +// plugin, or the session gets pg_codes that are not enabled for it (#159191). +const PLUGIN: PaymentPlugin = "e_commerce"; + type State = | { status: "idle" } | { status: "fetching_methods" } @@ -86,6 +91,7 @@ export default function CheckoutDemoInner() { try { const methodsResponse = await callPaymentMethods({ currencies: ["KWD"], + plugin: PLUGIN, type: "sandbox", tags: ["demo"], }); @@ -94,7 +100,7 @@ export default function CheckoutDemoInner() { const { session_id } = await createSandboxSession({ pg_codes: pgCodes.length > 0 ? pgCodes : ["ottu_sdk"], - type: "e_commerce", + type: PLUGIN, }); dispatch({ type: "SESSION_CREATED", sessionId: session_id }); } catch (err: any) { diff --git a/src/components/PaymentJourney/PaymentJourneyInner.tsx b/src/components/PaymentJourney/PaymentJourneyInner.tsx index 8e278c86..2736298a 100644 --- a/src/components/PaymentJourney/PaymentJourneyInner.tsx +++ b/src/components/PaymentJourney/PaymentJourneyInner.tsx @@ -6,6 +6,7 @@ import { callPaymentMethods, callPaymentStatusQuery, getWebhookBaseUrl, + type PaymentPlugin, } from "@site/src/utils/sandbox"; import { createDemoCallbacks } from "@site/src/utils/checkoutSdk"; import ApiPanel from "@site/src/components/ApiPanel"; @@ -15,6 +16,10 @@ import { TEST_CARD } from "@site/src/components/TestCardCallout"; import { COUNTRIES, DEFAULT_COUNTRY_INDEX } from "./countries"; import styles from "./styles.module.css"; +// Single-sourced: gateway discovery and session creation must name the same +// plugin, or the session gets pg_codes that are not enabled for it (#159191). +const PLUGIN: PaymentPlugin = "payment_request"; + // ── Types ────────────────────────────────────────────── type Status = @@ -325,7 +330,7 @@ export default function PaymentJourneyInner() { const runStep1 = useCallback(async () => { dispatch({ type: "COUNTRY_CONFIRMED" }); try { - const response = await callPaymentMethods({ currencies: [state.selectedCurrency], plugin: "payment_request", operation: "purchase", type: "sandbox", tags: ["demo"] }); + const response = await callPaymentMethods({ currencies: [state.selectedCurrency], plugin: PLUGIN, operation: "purchase", type: "sandbox", tags: ["demo"] }); const pgCodes = response?.payment_methods?.map((m: any) => m.code) ?? response?.pg_codes ?? []; dispatch({ type: "STEP1_DONE", pgCodes, response }); } catch (err: any) { @@ -341,6 +346,7 @@ export default function PaymentJourneyInner() { const webhookUrl = `${getWebhookBaseUrl()}/webhook/${state.orderId}`; const response = await createSandboxSession({ pg_codes: state.pgCodes.length > 0 ? state.pgCodes : ["direct-payment"], + type: PLUGIN, currency_code: state.selectedCurrency, customer_id: "sandbox", extra: { @@ -514,7 +520,7 @@ export default function PaymentJourneyInner() { {(state.status === "step1_done" || isStepExpanded(2)) && ( <> { const webhookUrl = `${getWebhookBaseUrl()}/webhook/${state.orderId}`; const mitRequest = { - type: "e_commerce", + type: PLUGIN, pg_codes: [state.citPgCode], amount: "15", currency_code: "KWD", @@ -387,6 +393,7 @@ export default function RecurringDemoInner() { try { const result = await createSandboxSession({ pg_codes: [state.citPgCode || state.pgCodes[0]], + type: PLUGIN, amount: "15", customer_id: state.customerId, extra: { @@ -411,7 +418,7 @@ export default function RecurringDemoInner() { const startMITTwoStep = useCallback(async () => { const webhookUrl = `${getWebhookBaseUrl()}/webhook/${state.orderId}`; const mitRequest = { - type: "e_commerce", + type: PLUGIN, pg_codes: [state.citPgCode], amount: "15", currency_code: "KWD", @@ -427,6 +434,7 @@ export default function RecurringDemoInner() { try { const session = await createSandboxSession({ pg_codes: [state.citPgCode || state.pgCodes[0]], + type: PLUGIN, amount: "15", customer_id: state.customerId, extra: { diff --git a/src/components/WalletDemo/WalletDemoInner.tsx b/src/components/WalletDemo/WalletDemoInner.tsx index 89201422..c0b03149 100644 --- a/src/components/WalletDemo/WalletDemoInner.tsx +++ b/src/components/WalletDemo/WalletDemoInner.tsx @@ -99,7 +99,7 @@ export default function WalletDemoInner() { try { // Step 1 — Fetch wallet-capable gateways - const filter = WALLET_DEMO.pgFilter || ({} as any); + const filter = WALLET_DEMO.pgFilter; const methodsResponse = await callPaymentMethods({ currencies: [WALLET_DEMO.currency], plugin: filter.plugin, @@ -131,7 +131,7 @@ export default function WalletDemoInner() { // Step 3 — Create Checkout session const { session_id } = await createSandboxSession({ pg_codes: [pgCode], - type: "e_commerce", + type: filter.plugin, amount: WALLET_DEMO.sessionAmount, currency_code: WALLET_DEMO.currency, customer_id: customerId, diff --git a/src/utils/sandbox.ts b/src/utils/sandbox.ts index 19fe0844..bedcbc37 100644 --- a/src/utils/sandbox.ts +++ b/src/utils/sandbox.ts @@ -38,12 +38,27 @@ export const KSA: ConnectEnv = { // WalletDemo seeds and charges in USD (see walletDemoConfig.ts). export const ACTIVE_CONNECT: ConnectEnv = SANDBOX; +/** + * The Ottu plugin a payment belongs to. + * + * Same vocabulary on both sides of the flow: it is the `plugin` filter on the + * Payment Methods API and the `type` of the Checkout API session. A gateway + * enabled for one plugin is not necessarily enabled for the other, so the two + * calls in a single demo must always use the same value. + */ +export type PaymentPlugin = "e_commerce" | "payment_request"; + export interface CreateSessionOptions { pg_codes: string[]; amount?: string; currency_code?: string; customer_id?: string; - type?: string; + /** + * Required for the same reason as `plugin` on `callPaymentMethods`: a session + * type that disagrees with the plugin the gateways were discovered for gets + * pg_codes the session cannot accept (#159191). + */ + type: PaymentPlugin; /** Arbitrary extra fields merged into the request body (e.g., payment_type, agreement, payment_instrument, webhook_url) */ extra?: Record; } @@ -79,7 +94,7 @@ export async function createSandboxSession( options: CreateSessionOptions ): Promise { const body = { - type: options.type ?? "payment_request", + type: options.type, pg_codes: options.pg_codes, amount: options.amount ?? "20", currency_code: options.currency_code ?? "KWD", @@ -151,7 +166,13 @@ export async function callAutoDebit( */ export async function callPaymentMethods(options: { currencies: string[]; - plugin?: string; + /** + * Required: gateways are enabled per plugin, so discovery must state which + * plugin the caller is about to create a session for. Defaulting this is what + * let CheckoutDemo discover `payment_request` gateways and then ask for an + * `e_commerce` session (#159191). + */ + plugin: PaymentPlugin; operation?: string; type?: string; tags?: string[]; @@ -160,7 +181,7 @@ export async function callPaymentMethods(options: { payment_services?: string[]; }): Promise { const body: Record = { - plugin: options.plugin ?? "payment_request", + plugin: options.plugin, operation: options.operation ?? "purchase", currencies: options.currencies, }; diff --git a/src/utils/walletDemoConfig.ts b/src/utils/walletDemoConfig.ts index f28524aa..53011b8c 100644 --- a/src/utils/walletDemoConfig.ts +++ b/src/utils/walletDemoConfig.ts @@ -6,13 +6,15 @@ * The merchant host / Api-Key / SDK key all come from `ACTIVE_CONNECT` * in `./sandbox` — flip that one global to retarget every demo on the site. */ +import type { PaymentPlugin } from "./sandbox"; + export const WALLET_DEMO = { // Wallet on sandbox.ottu.net is enabled for USD only (PG `ottu-sandbox-usd`). currency: "USD", seedAmount: "10.00", sessionAmount: "8.00", pgFilter: { - plugin: "e_commerce", + plugin: "e_commerce" as PaymentPlugin, type: "sandbox", tags: ["demo"], payment_services: ["wallet"],