fix(payment): route Philippine GCash separately from QRIS
Docker Image / Build and Push Docker Image (push) Successful in 2m12s
Docker Image / Build and Push Docker Image (push) Successful in 2m12s
This commit is contained in:
@@ -4,6 +4,7 @@ import { type Dispatch, useEffect, useRef, useState } from "react";
|
||||
|
||||
import {
|
||||
getPaymentUrl,
|
||||
getPaymentUrlHostname,
|
||||
getStripeClientSecret,
|
||||
isEzpayPayment,
|
||||
launchEzpayRedirect,
|
||||
@@ -41,31 +42,31 @@ export interface UsePaymentLaunchFlowInput {
|
||||
subscriptionType: PendingPaymentSubscriptionType;
|
||||
giftCategory?: string | null;
|
||||
giftPlanId?: string | null;
|
||||
countryCode?: string | null;
|
||||
}
|
||||
|
||||
export interface PaymentLaunchFlow {
|
||||
ezpayPaymentUrl: string | null;
|
||||
handleEzpayCancel: () => void;
|
||||
handleEzpayConfirm: () => void;
|
||||
handleQrisClose: () => void;
|
||||
handleQrisResume: () => void;
|
||||
handleRegionalQrClose: () => void;
|
||||
handleStripeClose: () => void;
|
||||
handleStripeConfirmed: () => void;
|
||||
hasHiddenQrisPayment: boolean;
|
||||
isConfirmingEzpay: boolean;
|
||||
qrisErrorMessage: string | null;
|
||||
qrisPayment: QrisPaymentDetails | null;
|
||||
qrisStatus: PaymentContextState["orderStatus"];
|
||||
regionalQrErrorMessage: string | null;
|
||||
regionalQrPayment: RegionalQrPaymentDetails | null;
|
||||
regionalQrStatus: PaymentContextState["orderStatus"];
|
||||
resetLaunchState: () => void;
|
||||
shouldShowEzpayConfirmDialog: boolean;
|
||||
shouldShowQrisDialog: boolean;
|
||||
shouldShowRegionalQrDialog: boolean;
|
||||
shouldShowStripeDialog: boolean;
|
||||
stripeClientSecret: string | null;
|
||||
}
|
||||
|
||||
export interface QrisPaymentDetails {
|
||||
export interface RegionalQrPaymentDetails {
|
||||
amountCents: number;
|
||||
currency: string;
|
||||
experience: "gcashQrPh" | "qris" | "paymentQr";
|
||||
orderId: string;
|
||||
qrData: string;
|
||||
}
|
||||
@@ -155,30 +156,36 @@ export function usePaymentLaunchFlow({
|
||||
subscriptionType,
|
||||
giftCategory,
|
||||
giftPlanId,
|
||||
countryCode,
|
||||
}: UsePaymentLaunchFlowInput): PaymentLaunchFlow {
|
||||
const launchedNonceRef = useRef(0);
|
||||
const [hiddenStripeClientSecret, setHiddenStripeClientSecret] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
const [isConfirmingEzpay, setIsConfirmingEzpay] = useState(false);
|
||||
const [hiddenQrisOrderId, setHiddenQrisOrderId] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
const stripeClientSecret = payment.payParams
|
||||
? getStripeClientSecret(payment.payParams)
|
||||
: null;
|
||||
const selectedPlan = payment.plans.find(
|
||||
(item) => item.planId === payment.selectedPlanId,
|
||||
);
|
||||
const paymentCurrency = payment.payParams
|
||||
? paymentParamString(payment.payParams, "currency") ??
|
||||
selectedPlan?.currency ??
|
||||
null
|
||||
: selectedPlan?.currency ?? null;
|
||||
const ezpayLaunchTarget =
|
||||
payment.payParams && isEzpayPayment(payment.payParams)
|
||||
? resolveEzpayLaunchTarget(payment.payParams)
|
||||
? resolveEzpayLaunchTarget(payment.payParams, {
|
||||
countryCode,
|
||||
currency: paymentCurrency,
|
||||
})
|
||||
: null;
|
||||
const ezpayPaymentUrl =
|
||||
ezpayLaunchTarget?.kind === "url"
|
||||
? ezpayLaunchTarget.paymentUrl
|
||||
: null;
|
||||
const selectedPlan = payment.plans.find(
|
||||
(item) => item.planId === payment.selectedPlanId,
|
||||
);
|
||||
const qrisPayment: QrisPaymentDetails | null =
|
||||
const regionalQrPayment: RegionalQrPaymentDetails | null =
|
||||
payment.payParams &&
|
||||
payment.currentOrderId &&
|
||||
ezpayLaunchTarget?.kind === "qr"
|
||||
@@ -188,9 +195,9 @@ export function usePaymentLaunchFlow({
|
||||
selectedPlan?.amountCents ??
|
||||
0,
|
||||
currency:
|
||||
paymentParamString(payment.payParams, "currency") ??
|
||||
selectedPlan?.currency ??
|
||||
"IDR",
|
||||
paymentCurrency ??
|
||||
(ezpayLaunchTarget.experience === "gcashQrPh" ? "PHP" : "IDR"),
|
||||
experience: ezpayLaunchTarget.experience,
|
||||
orderId: payment.currentOrderId,
|
||||
qrData: ezpayLaunchTarget.qrData,
|
||||
}
|
||||
@@ -209,7 +216,33 @@ export function usePaymentLaunchFlow({
|
||||
|
||||
const isEzpay = isEzpayPayment(payment.payParams);
|
||||
if (isEzpay) {
|
||||
const target = resolveEzpayLaunchTarget(payment.payParams);
|
||||
const target = resolveEzpayLaunchTarget(payment.payParams, {
|
||||
countryCode,
|
||||
currency: paymentCurrency,
|
||||
});
|
||||
const channelType = paymentParamString(
|
||||
payment.payParams,
|
||||
"channelType",
|
||||
"channel_type",
|
||||
);
|
||||
const channelCode = paymentParamString(
|
||||
payment.payParams,
|
||||
"channelCode",
|
||||
"channel_code",
|
||||
);
|
||||
const namedPaymentUrl = getPaymentUrl(payment.payParams);
|
||||
log.debug(`[${logScope}] ezpay launch target resolved`, {
|
||||
countryCode: countryCode?.trim().toUpperCase() ?? null,
|
||||
currency: paymentCurrency?.trim().toUpperCase() ?? null,
|
||||
channelType: channelType?.toUpperCase() ?? null,
|
||||
channelCode,
|
||||
hasCashierUrl: getPaymentUrlHostname(namedPaymentUrl) !== null,
|
||||
hasQrData: target.kind === "qr",
|
||||
paymentUrlHost:
|
||||
target.kind === "url"
|
||||
? getPaymentUrlHostname(target.paymentUrl)
|
||||
: null,
|
||||
});
|
||||
if (target.kind === "error") {
|
||||
trackPaymentCheckoutFailed(payment, "missing_checkout_url");
|
||||
paymentDispatch({
|
||||
@@ -224,11 +257,18 @@ export function usePaymentLaunchFlow({
|
||||
trackPaymentCheckoutFailed(payment, "missing_checkout_url");
|
||||
paymentDispatch({
|
||||
type: "PaymentLaunchFailed",
|
||||
errorMessage: "Missing order id before showing QRIS.",
|
||||
errorMessage: "Missing order id before showing payment QR code.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
trackPaymentCheckoutOpened(payment, "qris_embedded");
|
||||
trackPaymentCheckoutOpened(
|
||||
payment,
|
||||
target.experience === "qris"
|
||||
? "qris_embedded"
|
||||
: target.experience === "gcashQrPh"
|
||||
? "gcash_qrph_embedded"
|
||||
: "payment_qr_embedded",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -236,7 +276,7 @@ export function usePaymentLaunchFlow({
|
||||
if (!AppEnvUtil.isProduction()) {
|
||||
log.debug(`[${logScope}] ezpay confirmation required`, {
|
||||
orderId: payment.currentOrderId,
|
||||
paymentUrl,
|
||||
paymentUrlHost: getPaymentUrlHostname(paymentUrl),
|
||||
subscriptionType,
|
||||
});
|
||||
return;
|
||||
@@ -262,7 +302,7 @@ export function usePaymentLaunchFlow({
|
||||
const paymentUrl = getPaymentUrl(payment.payParams);
|
||||
if (paymentUrl) {
|
||||
try {
|
||||
window.location.href = paymentUrl;
|
||||
window.location.assign(paymentUrl);
|
||||
trackPaymentCheckoutOpened(payment, paymentUrl);
|
||||
} catch {
|
||||
trackPaymentCheckoutFailed(payment, "payment_redirect_failed");
|
||||
@@ -283,6 +323,7 @@ export function usePaymentLaunchFlow({
|
||||
log,
|
||||
logScope,
|
||||
characterSlug,
|
||||
countryCode,
|
||||
payment.currentOrderId,
|
||||
payment,
|
||||
payment.launchNonce,
|
||||
@@ -291,6 +332,7 @@ export function usePaymentLaunchFlow({
|
||||
payment.plans,
|
||||
payment.selectedPlanId,
|
||||
paymentDispatch,
|
||||
paymentCurrency,
|
||||
returnTo,
|
||||
subscriptionType,
|
||||
giftCategory,
|
||||
@@ -307,22 +349,13 @@ export function usePaymentLaunchFlow({
|
||||
payParams: payment.payParams,
|
||||
paymentUrl: ezpayPaymentUrl,
|
||||
});
|
||||
const shouldShowQrisDialog = Boolean(
|
||||
qrisPayment &&
|
||||
qrisPayment.orderId !== hiddenQrisOrderId &&
|
||||
!payment.isPaid,
|
||||
);
|
||||
const hasHiddenQrisPayment = Boolean(
|
||||
qrisPayment &&
|
||||
qrisPayment.orderId === hiddenQrisOrderId &&
|
||||
payment.isPollingOrder &&
|
||||
!payment.isPaid,
|
||||
const shouldShowRegionalQrDialog = Boolean(
|
||||
regionalQrPayment && !payment.isPaid,
|
||||
);
|
||||
|
||||
function resetLaunchState(): void {
|
||||
setIsConfirmingEzpay(false);
|
||||
setHiddenStripeClientSecret(null);
|
||||
setHiddenQrisOrderId(null);
|
||||
}
|
||||
|
||||
function handleStripeClose(): void {
|
||||
@@ -369,39 +402,29 @@ export function usePaymentLaunchFlow({
|
||||
paymentDispatch({ type: "PaymentReset" });
|
||||
}
|
||||
|
||||
function handleQrisClose(): void {
|
||||
log.debug(`[${logScope}] qris dialog closed`, {
|
||||
orderId: qrisPayment?.orderId ?? payment.currentOrderId,
|
||||
function handleRegionalQrClose(): void {
|
||||
log.debug(`[${logScope}] regional payment QR dialog closed`, {
|
||||
orderId: regionalQrPayment?.orderId ?? payment.currentOrderId,
|
||||
experience: regionalQrPayment?.experience ?? null,
|
||||
subscriptionType,
|
||||
});
|
||||
setHiddenQrisOrderId(qrisPayment?.orderId ?? payment.currentOrderId);
|
||||
}
|
||||
|
||||
function handleQrisResume(): void {
|
||||
if (!hasHiddenQrisPayment) return;
|
||||
log.debug(`[${logScope}] qris dialog resumed`, {
|
||||
orderId: qrisPayment?.orderId ?? payment.currentOrderId,
|
||||
subscriptionType,
|
||||
});
|
||||
setHiddenQrisOrderId(null);
|
||||
paymentDispatch({ type: "PaymentReset" });
|
||||
}
|
||||
|
||||
return {
|
||||
ezpayPaymentUrl,
|
||||
handleEzpayCancel,
|
||||
handleEzpayConfirm,
|
||||
handleQrisClose,
|
||||
handleQrisResume,
|
||||
handleRegionalQrClose,
|
||||
handleStripeClose,
|
||||
handleStripeConfirmed,
|
||||
hasHiddenQrisPayment,
|
||||
isConfirmingEzpay,
|
||||
qrisErrorMessage: payment.errorMessage,
|
||||
qrisPayment,
|
||||
qrisStatus: payment.orderStatus,
|
||||
regionalQrErrorMessage: payment.errorMessage,
|
||||
regionalQrPayment,
|
||||
regionalQrStatus: payment.orderStatus,
|
||||
resetLaunchState,
|
||||
shouldShowEzpayConfirmDialog,
|
||||
shouldShowQrisDialog,
|
||||
shouldShowRegionalQrDialog,
|
||||
shouldShowStripeDialog,
|
||||
stripeClientSecret,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user