How It Works
This integration connects Stripe’s Payment Element with HitPay’s Payment Request API, allowing customers to pay using local payment methods while keeping all transaction records in Stripe.Key Concepts
| Component | Purpose |
|---|---|
| Stripe PaymentIntent | Tracks the payment intent on Stripe’s side. Remains “incomplete” for external payments. |
| HitPay Payment Request | Processes the actual payment via local methods (PayNow, ShopeePay, etc.) and generates QR codes. |
| Stripe Payment Records | Records the external HitPay payment back in Stripe for unified reporting and reconciliation. |
API References
HitPay Payment Request API
Create payment requests and generate QR codes for local payment methods.
HitPay Embedded QR Payments
Embed QR codes directly in your checkout for seamless mobile payments.
Payment Flow
Step 1: Configure Custom Payment Methods
Register each HitPay payment method as a Custom Payment Method (CPM) type in your Stripe Dashboard, map those CPM Type IDs to their HitPay method identifiers in a config file, and load them into the Stripe Elements provider so they appear as options in your checkout.Create Custom Payment Methods on Stripe Dashboard
Stripe DashboardCreate Custom Payment Method types in your Stripe Dashboard for each HitPay payment method you want to offer.After creating the custom payment method, the Dashboard displays the custom payment method ID (beginning with cpmt_) that you need for the next step.
Create CPM in Stripe Dashboard
Follow Stripe’s guide to create Custom Payment Method types and get your CPM Type IDs.
Download Payment Icons
Download official HitPay payment method icons (PayNow, ShopeePay, GrabPay, FPX, and more) optimized for Stripe Custom Payment Methods.
Create Configuration File
Server-sideThis configuration file maps the Custom Payment Methods you created in the Stripe Dashboard to their corresponding HitPay payment method identifiers. Each entry links a Stripe CPM Type ID (e.g.
cpmt_xxx) to the HitPay API method name used when creating a payment request.See the HitPay Payment Methods Reference for the full list of supported payment method names and their identifiers.The chargeAutomatically flag indicates whether a payment method supports tokenized or recurring payments — these methods can be used to charge customers automatically without requiring re-authorization each time.The code below is a sample — adapt it to the payment methods you’ve configured:config/payment-methods.ts
config/payment-methods.ts
Add Custom Payment Method Type to Stripe Elements Configuration
Client-sideNext, add the custom payment method type to your Stripe Elements configuration. In your
After loading, the Payment Element shows your custom payment method.
checkout.js file where you initialise Stripe Elements, specify the customPaymentMethods to add to the Payment Element. Provide the custom payment method ID from the previous step, the options.type and an optional subtitle.Load Stripe.js with the beta flag:lib/stripe-client.ts
lib/stripe-client.ts
Add CPMs to Elements provider
Add CPMs to Elements provider
Add CPM to Elements
Learn more about configuring Custom Payment Methods in the Payment Element.
Step 2: Display Payment on Stripe Payment Elements
When a customer selects a HitPay payment method in the Payment Element, your backend creates a HitPay payment request and returns a QR code or deep link for the customer to complete payment on their mobile device.Configure Environment Variables
Server-sideSet up the required API keys and configuration for both Stripe and HitPay. These credentials authenticate your server with both payment platforms.
Create HitPay Payment Request
Server-sideWhen a user selects a CPM in the Payment Element, create a HitPay payment request and display the QR code:
app/api/hitpay/create/route.ts
app/api/hitpay/create/route.ts
App-based methods (ShopeePay, GrabPay): Some payment methods don’t generate a QR code — instead, HitPay returns a
direct_link.direct_link_url that opens the payment app directly. When directLinkUrl is present, redirect the customer to that URL instead of displaying a QR code.Embedded QR Code Payments
Learn how to embed and display QR codes for PayNow, ShopeePay, and other supported payment methods.
Step 3: Record Payment Confirmation
Once the customer pays via HitPay, your backend receives a webhook, verifies the payment, and records it in Stripe using the Payment Records API — making it visible in your Stripe Dashboard alongside native card payments.Without this step, HitPay payments only appear in your HitPay Dashboard. By recording them via Stripe’s Payment Records API, you get a single source of truth for all transactions — cards, PayNow, ShopeePay, and more — all in one place.
Stripe Payment Records API
Learn how Payment Records work and how they appear in your Stripe Dashboard.
Listen for Payment Confirmation
Server-side
- Webhooks (Recommended)
- Polling (Not Recommended)
Use webhooks for production. HitPay automatically sends a POST request to your server when payment is completed.How it works:
- Customer completes payment via HitPay (scans QR, etc.)
- HitPay sends webhook to your
/api/hitpay/webhookendpoint - Your server verifies the signature and confirms payment status
- Your server creates a Payment Record in Stripe
- Transaction now appears in Stripe Dashboard for reconciliation
| Advantage | Description |
|---|---|
| Reliable | Payments recorded even if user closes browser |
| Real-time | Instant notification on payment completion |
| Secure | HMAC signature verification prevents fraud |
| Unified Dashboard | All transactions visible in Stripe |
Frontend completion detection: Even when using webhooks for payment recording, your frontend still needs a way to know when the payment is complete so it can redirect the customer. The recommended pattern is to have the frontend poll a status endpoint (e.g.,
/api/payment/check-status) every few seconds — this endpoint checks whether the webhook has already recorded the payment. Use an idempotency key (e.g., prec-{paymentRequestId}) when creating the Payment Record to prevent double-recording if both webhook and polling race.HitPay Webhooks Guide
Configure webhooks in your HitPay Dashboard.
app/api/hitpay/webhook/route.ts
app/api/hitpay/webhook/route.ts
Record the Payment to Your Stripe Account
Server-sideOnce HitPay confirms the payment is complete, call Each field in the payload serves a specific purpose:
stripe.paymentRecords.reportPayment() to create a Payment Record (prec_xxx) in Stripe. This is what makes the transaction visible in your Stripe Dashboard alongside native card payments.The original PaymentIntent remains
"Incomplete" — this is expected for external payments. The Payment Record is the canonical record of the transaction and what Stripe uses for revenue reporting.| Field | Value | Why |
|---|---|---|
amount_requested | PaymentIntent amount + currency | Ties the record to the original order amount |
payment_method_details.payment_method | Custom PaymentMethod ID (pm_xxx) | Links the CPM type (PayNow, ShopeePay, etc.) to this record |
processor_details.custom.payment_reference | HitPay payment ID | External transaction reference for reconciliation |
initiated_at | Unix timestamp | When the payment was initiated |
customer_presence | 'on_session' | Customer was actively present at time of payment |
outcome | 'guaranteed' | HitPay has confirmed settlement — funds are guaranteed |
guaranteed.guaranteed_at | Unix timestamp | When settlement was confirmed by HitPay |
metadata | HitPay IDs, PI ID | Searchable references for support and debugging |
Record payment — stripe.paymentRecords.reportPayment()
Record payment — stripe.paymentRecords.reportPayment()
Testing
Sandbox Testing
- Set
NEXT_PUBLIC_HITPAY_ENV=sandboxin your environment - Use HitPay sandbox API credentials
- When a QR code is displayed, HitPay provides a “Complete Mock Payment” link
- Click this link to simulate a successful payment
Production Testing
- Switch to production credentials
- Use real payment apps to scan QR codes
- Verify payments appear in both HitPay and Stripe dashboards
FAQ
Why isn't my CPM showing in the Payment Element?
Why isn't my CPM showing in the Payment Element?
- Verify the CPM Type is enabled in Stripe Dashboard
- Check that the CPM ID in your config matches the Dashboard
- Ensure you’re loading Stripe.js with
custom_payment_methods_beta_1
Why isn't the QR code appearing?
Why isn't the QR code appearing?
- Not all HitPay methods support QR codes (e.g., GrabPay uses redirect)
- Check the
checkoutUrlfallback is being displayed - Verify the payment method is enabled in HitPay Dashboard
Why am I getting a HitPay 422 validation error?
Why am I getting a HitPay 422 validation error?
- The payment method may not be enabled in your HitPay account
- Some methods aren’t available in sandbox mode
- Check the error message for specific details
Why isn't the payment recording in Stripe?
Why isn't the payment recording in Stripe?
- Check server logs for Payment Records API errors
- Verify your Stripe API version includes the beta flag
- The payment still succeeded if HitPay shows completed