OnePay System Migration Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Remove all VNPay references and make OnePay the sole payment gateway.
Architecture: OnePay is already fully integrated in the backend (packages/backend/convex/http/onepay.ts). This migration removes VNPay from the frontend hooks, schema, and backend functions.
Tech Stack: Next.js 16, Convex, React Hooks, Zod
File Structure
Files to DELETE:
apps/frontend/lib/hooks/use-vnpay-handler.tspackages/backend/convex/functions/vnpay.http.ts
Files to MODIFY:
apps/frontend/lib/hooks/use-payment-handlers.ts— RemovehandleVNPayPaymentapps/frontend/lib/hooks/use-reservation-detail.ts— RemovevnpayTransactionIdtypeapps/frontend/app/[locale]/booking/payment/page.tsx— Remove VNPay payment optionpackages/backend/convex/schema.ts— RemovevnpayTransactionIdfieldpackages/backend/convex/functions/reservations.ts— RemovevnpayTransactionIdfromconfirmPaymentpackages/backend/convex/http.ts— Remove vnpay routepackages/backend/convex/functions/crm_sync.ts— Replace VNPay with OnePaypackages/backend/convex/functions/crm_sync_internal.ts— Replace VNPay with OnePayapps/frontend/messages/vi.json— Update i18n stringsapps/frontend/messages/en.json— Update i18n strings
Tasks
Task 1: Update use-payment-handlers.ts — Remove VNPay Handler
Files:
-
Modify:
apps/frontend/lib/hooks/use-payment-handlers.ts:1-151 -
Step 1: Read current file to understand full content
cat apps/frontend/lib/hooks/use-payment-handlers.ts- Step 2: Update comment header to remove VNPay mention
// ipsoc checked: 2026-05-05
// SoC: Payment gateway orchestration (OnePay only)
// This hook accepts mutation functions to keep HTTP call logic in the hook- Step 3: Remove
handleVNPayPaymentfunction and itsvnpayTransactionIdlogic
The function to remove (lines 49-111):
const handleVNPayPayment = useCallback(async () => {
setIsProcessing(true);
try {
const reservationId = draft?.reservationId;
if (!reservationId) {
throw new Error("No reservation found");
}
const customerInfo = draft?.customerInfo;
if (!customerInfo?.email || !customerInfo?.firstName) {
throw new Error("Customer info not complete");
}
if (!occurrenceData?.occurrence || !occurrenceData?.template) {
throw new Error("Show information not available");
}
const { occurrence, template } = occurrenceData;
const { subject: emailSubject, html: emailHtml } =
await buildBookingConfirmationEmail({
locale,
customerFirstName: customerInfo.firstName,
showTitle: template.title,
showDate: occurrence.date,
showTime: occurrence.time,
ticketType: draft?.ticketType ?? "DINNER_THEATRE",
quantity: draft?.quantity ?? 1,
totalAmount: 0,
qrCode: undefined,
});
// Simulate VNPay API call delay
await new Promise((resolve) => setTimeout(resolve, 1500));
// Generate a mock transaction ID (in production, this comes from VNPay)
const vnpayTransactionId = `VNPAY_${Date.now()}_${reservationId}`;
// Confirm payment in Convex with pre-rendered email content
await confirmPayment({
reservationId,
vnpayTransactionId,
emailSubject,
emailHtml,
});
onSuccess?.(reservationId);
router.push(
getLocalePath(
`/booking/confirmation?reservationId=${reservationId}`,
locale,
),
);
} catch (error) {
const message =
error instanceof Error
? error.message
: "Payment failed. Please try again.";
toast.error(message);
setIsProcessing(false);
}
}, [router, onSuccess, locale, draft, occurrenceData, confirmPayment]);- Step 4: Remove
handleVNPayPaymentfrom return object
Change:
return {
isProcessing,
handleVNPayPayment,
handleOnePayPayment,
};To:
return {
isProcessing,
handleOnePayPayment,
};- Step 5: Verify and commit
git diff apps/frontend/lib/hooks/use-payment-handlers.tsTask 2: Delete use-vnpay-handler.ts
Files:
-
Delete:
apps/frontend/lib/hooks/use-vnpay-handler.ts -
Step 1: Delete the file
rm apps/frontend/lib/hooks/use-vnpay-handler.ts- Step 2: Verify file is deleted and no imports remain
ls apps/frontend/lib/hooks/use-vnpay-handler.ts 2>&1 || echo "File deleted"
git grep "use-vnpay-handler" apps/frontend/Task 3: Update use-reservation-detail.ts — Remove vnpayTransactionId Type
Files:
-
Modify:
apps/frontend/lib/hooks/use-reservation-detail.ts -
Step 1: Read file to find vnpayTransactionId usage
grep -n "vnpayTransactionId" apps/frontend/lib/hooks/use-reservation-detail.ts- Step 2: Remove vnpayTransactionId from the type definition
The type definition to update:
paymentStatus: "PENDING" | "PAID" | "REFUNDED" | "FAILED";
paymentGateway?: string;
vnpayTransactionId?: string; // REMOVE THIS LINE
onePayTransactionId?: string;After removal:
paymentStatus: "PENDING" | "PAID" | "REFUNDED" | "FAILED";
paymentGateway?: string;
onePayTransactionId?: string;- Step 3: Verify no remaining vnpay references
grep -i "vnpay" apps/frontend/lib/hooks/use-reservation-detail.tsTask 4: Update Payment Page — Remove VNPay UI Option
Files:
-
Modify:
apps/frontend/app/[locale]/booking/payment/page.tsx -
Step 1: Update migration header comment
Change:
/**
* Booking Payment — migrated from WordPress purchase-v4.php
* Source: apps/frontend/raw/ticket-booking/purchase-v4.php
* Source lines: (PHP file - booking flow step)
*
* Migration notes:
* - Payment processing step of booking flow
* - VNPay/OnePay integration placeholder
* - Uses booking context for state management
* - Payment gateway integration (Phase 3)
*/To:
/**
* Booking Payment — migrated from WordPress purchase-v4.php
* Source: apps/frontend/raw/ticket-booking/purchase-v4.php
* Source lines: (PHP file - booking flow step)
*
* Migration notes:
* - Payment processing step of booking flow
* - OnePay integration (VNPay removed)
* - Uses booking context for state management
*/- Step 2: Remove handleVNPayPayment from destructuring
Change:
const { isProcessing, handleVNPayPayment, handleOnePayPayment } =
usePaymentHandlers({}, { createOnePayOrder });To:
const { isProcessing, handleOnePayPayment } = usePaymentHandlers(
{},
{ createOnePayOrder },
);- Step 3: Remove VNPay Card from payment methods
Remove the entire Card block for VNPay (lines 111-134):
<Card
className={cn(
"cursor-pointer hover:shadow-md transition-all",
isProcessing && "opacity-50",
)}
onClick={!isProcessing ? handleVNPayPayment : undefined}
>
<CardContent className="flex items-center gap-4 p-6">
<div className="bg-blue-600 text-white p-3 rounded-lg">
<CreditCard className="h-6 w-6" />
</div>
<div className="flex-1">
<div className="font-semibold">VNPay</div>
<div className="text-sm text-muted-foreground">
Pay securely with VNPay
</div>
</div>
{isProcessing ? (
<Loader2 className="h-5 w-5 animate-spin" />
) : (
<CheckCircle className="h-5 w-5 text-muted-foreground" />
)}
</CardContent>
</Card>- Step 4: Update footer text
Change:
<p>Your payment is secured by VNPay and OnePay</p>To:
<p>Your payment is secured by OnePay</p>- Step 5: Verify changes and commit
git diff apps/frontend/app/[locale]/booking/payment/page.tsxTask 5: Update Convex Schema — Remove vnpayTransactionId
Files:
-
Modify:
packages/backend/convex/schema.ts -
Step 1: Read reservations table definition
grep -n "vnpayTransactionId" packages/backend/convex/schema.ts- Step 2: Remove vnpayTransactionId field from reservations table
Remove line:
vnpayTransactionId: v.optional(v.string()),The reservations table fields around this area:
onePayOrderId: v.optional(v.string()),
onePayTransactionId: v.optional(v.string()),
vaNumber: v.optional(v.string()),
vnpayTransactionId: v.optional(v.string()), // REMOVE THIS LINE
qrCode: v.optional(v.string()),- Step 3: Verify no remaining vnpay references in schema
grep -i "vnpay" packages/backend/convex/schema.tsTask 6: Update Reservations Mutation — Remove vnpayTransactionId Parameter
Files:
-
Modify:
packages/backend/convex/functions/reservations.ts -
Step 1: Find confirmPayment mutation
grep -n "vnpayTransactionId" packages/backend/convex/functions/reservations.ts- Step 2: Read confirmPayment function
The function currently has:
export const confirmPayment = zMutation({
args: {
reservationId: zid("reservations"),
vnpayTransactionId: z.string(),
emailSubject: z.string(),
emailHtml: z.string(),
},
handler: async (ctx, { reservationId, vnpayTransactionId, emailSubject, emailHtml }) => {- Step 3: Rename parameter and update usage
Change:
vnpayTransactionId: z.string(),To:
onePayTransactionId: z.string(),And update the handler to use onePayTransactionId:
onePayTransactionId,- Step 4: Update db.patch call
Find and update:
vnpayTransactionId,To:
onePayTransactionId: onePayTransactionId,- Step 5: Verify changes
grep -n "vnpayTransactionId" packages/backend/convex/functions/reservations.tsTask 7: Delete vnpay.http.ts
Files:
-
Delete:
packages/backend/convex/functions/vnpay.http.ts -
Step 1: Delete the file
rm packages/backend/convex/functions/vnpay.http.ts- Step 2: Verify deletion and no imports
ls packages/backend/convex/functions/vnpay.http.ts 2>&1 || echo "File deleted"
git grep "vnpay.http" packages/backend/convex/Task 8: Update http.ts — Remove vnpay Route
Files:
-
Modify:
packages/backend/convex/http.ts -
Step 1: Read http.ts
cat packages/backend/convex/http.ts- Step 2: Remove vnpay route
Remove the import:
import { vnpayHttpAction } from "./functions/vnpay.http";Remove the route:
http.route({
path: "/vnpay/payment",
method: "POST",
handler: vnpayHttpAction,
}),- Step 3: Verify changes
grep -i "vnpay" packages/backend/convex/http.tsTask 9: Update CRM Sync — Replace VNPay with OnePay
Files:
-
Modify:
packages/backend/convex/functions/crm_sync.ts -
Modify:
packages/backend/convex/functions/crm_sync_internal.ts -
Step 1: Check vnpayTransactionId usage in crm_sync.ts
grep -n "vnpayTransactionId\|VNPay" packages/backend/convex/functions/crm_sync.ts- Step 2: Update all VNPay references to OnePay
In crm_sync.ts:
- Change
vnpayTransactionIdtoonePayTransactionId - Change
payment_mode: "VNPay"topayment_mode: "OnePay"
In crm_sync_internal.ts:
-
Change
vnpayTransactionIdtoonePayTransactionId -
Change
payment_mode: "VNPay"topayment_mode: "OnePay" -
Step 3: Verify no remaining VNPay references
grep -i "vnpay\|VNPay" packages/backend/convex/functions/crm_sync.ts
grep -i "vnpay\|VNPay" packages/backend/convex/functions/crm_sync_internal.tsTask 10: Update i18n Strings
Files:
-
Modify:
apps/frontend/messages/vi.json -
Modify:
apps/frontend/messages/en.json -
Step 1: Find VNPay references in i18n files
grep -n "vnpay\|VNPay" apps/frontend/messages/vi.json apps/frontend/messages/en.json- Step 2: Update VNPay strings to OnePay
Replace any display strings:
-
"Pay securely with VNPay"→"Pay securely with OnePay" -
"VNPay"→"OnePay"(in payment-related contexts) -
"Your payment is secured by VNPay and OnePay"→"Your payment is secured by OnePay" -
Step 3: Verify changes
grep -i "vnpay\|VNPay" apps/frontend/messages/vi.json apps/frontend/messages/en.jsonTask 11: Final Verification
- Step 1: Full project grep for remaining vnpay references
grep -ri "vnpay" apps/frontend/ packages/backend/convex/ --include="*.ts" --include="*.tsx" --include="*.json" | grep -v node_modules | grep -v ".git"- Step 2: Verify no remaining VNPay imports in hooks
grep -r "use-vnpay-handler\|vnpay.http" apps/frontend/ packages/backend/convex/- Step 3: Type check
cd apps/frontend && npx tsc --noEmit 2>&1 | head -50- Step 4: Convex deploy (if changes to backend)
cd packages/backend/convex && npx convex deploy --dryMigration Notes
- OnePay integration already exists in
packages/backend/convex/http/onepay.ts - The
confirmPaymentmutation is renamed to useonePayTransactionIdparameter - CRM sync fields updated from VNPay to OnePay
- All UI references to VNPay removed from payment page