plans
2026-05-05
2026 05 05 Onepay System

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.ts
  • packages/backend/convex/functions/vnpay.http.ts

Files to MODIFY:

  • apps/frontend/lib/hooks/use-payment-handlers.ts — Remove handleVNPayPayment
  • apps/frontend/lib/hooks/use-reservation-detail.ts — Remove vnpayTransactionId type
  • apps/frontend/app/[locale]/booking/payment/page.tsx — Remove VNPay payment option
  • packages/backend/convex/schema.ts — Remove vnpayTransactionId field
  • packages/backend/convex/functions/reservations.ts — Remove vnpayTransactionId from confirmPayment
  • packages/backend/convex/http.ts — Remove vnpay route
  • packages/backend/convex/functions/crm_sync.ts — Replace VNPay with OnePay
  • packages/backend/convex/functions/crm_sync_internal.ts — Replace VNPay with OnePay
  • apps/frontend/messages/vi.json — Update i18n strings
  • apps/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 handleVNPayPayment function and its vnpayTransactionId logic

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 handleVNPayPayment from 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.ts

Task 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.ts

Task 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.tsx

Task 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.ts

Task 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.ts

Task 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.ts

Task 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 vnpayTransactionId to onePayTransactionId
  • Change payment_mode: "VNPay" to payment_mode: "OnePay"

In crm_sync_internal.ts:

  • Change vnpayTransactionId to onePayTransactionId

  • Change payment_mode: "VNPay" to payment_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.ts

Task 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.json

Task 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 --dry

Migration Notes

  • OnePay integration already exists in packages/backend/convex/http/onepay.ts
  • The confirmPayment mutation is renamed to use onePayTransactionId parameter
  • CRM sync fields updated from VNPay to OnePay
  • All UI references to VNPay removed from payment page