Modul praktis Level 2 VibeKoding: Integrasi Pembayaran Stripe: Cara Memungut Duit.Modul praktis Level 2 VibeKoding: Integrasi Pembayaran Stripe: Cara Memungut Duit.
When your product already has pages, authentication, a database, and a basic backend, the next practical question is: how do you charge for it.When your product already has pages, authentication, a database, and a basic backend, the next practical question is: how do you charge for it.
Many people making their first payment integration focus entirely on "how to redirect to the payment page." But what truly determines whether the system is stable is not the button -- it's the entire billing chain: who decides the price, who confirms the payment succeeded, who updates the database, and who grants or revokes access.Many people making their first payment integration focus entirely on "how to redirect to the payment page." But what truly determines whether the system is stable is not the button -- it's the entire billing chain: who decides the price, who confirms the payment succeeded, who updates the database, and who grants or revokes access.
This article is split into two parts:This article is split into two parts:
> ๐ก We recommend completing these chapters before continuing:> ๐ก We recommend completing these chapters before continuing:
>>
> - [From Database to Supabase](../database-supabase/)> - [From Database to Supabase](../database-supabase/)
> - [Using AI to Write API Code and Documentation](../ai-interface-code/)> - [Using AI to Write API Code and Documentation](../ai-interface-code/)
> - [How to Deploy a Web Application](../zeabur-deployment/)> - [How to Deploy a Web Application](../zeabur-deployment/)
------
If you only remember three things, remember these:If you only remember three things, remember these:
success page.What actually grants access is the Webhook, not the success page.These three principles are the core boundaries of any payment system. As long as the boundaries are correct, switching between Stripe, PayPal, Alipay, or WeChat Pay is essentially just "the API changes, but the architecture stays the same."These three principles are the core boundaries of any payment system. As long as the boundaries are correct, switching between Stripe, PayPal, Alipay, or WeChat Pay is essentially just "the API changes, but the architecture stays the same."
This is the most natural idea many people have when building payments for the first time:This is the most natural idea many people have when building payments for the first time:
If you're just building a fake demo page, this thinking is fine.If you're just building a fake demo page, this thinking is fine.
But if you're actually collecting real money, this approach usually leads to trouble.But if you're actually collecting real money, this approach usually leads to trouble.
The most common problems are:The most common problems are:
Requests from the browser are sent from the user's own computer. Others can modify the request content.Requests from the browser are sent from the user's own computer. Others can modify the request content.
Truly important keys, pricing logic, and membership activation logic should never be on the frontend.Truly important keys, pricing logic, and membership activation logic should never be on the frontend.
The user landing on a success page doesn't mean your database has been synced correctly.The user landing on a success page doesn't mean your database has been synced correctly.
The user might say "I already paid," but your own system has no record of it.The user might say "I already paid," but your own system has no record of it.
So the safer division of labor should be:So the safer division of labor should be:
The frontend can handle redirections; the backend must handle pricing and confirmation. As long as real money is involved, never put the "final pricing authority" and "post-payment activation logic" on the frontend.The frontend can handle redirections; the backend must handle pricing and confirmation. As long as real money is involved, never put the "final pricing authority" and "post-payment activation logic" on the frontend.
If you're building any of the following scenarios, Stripe is usually the smoothest starting point:If you're building any of the following scenarios, Stripe is usually the smoothest starting point:
If your primary users are in mainland China, Stripe usually wouldn't be your first choice -- I'll cover that in the appendix.If your primary users are in mainland China, Stripe usually wouldn't be your first choice -- I'll cover that in the appendix.
Let's start with the minimum version. As long as this chain works, your payment system has a skeleton.Let's start with the minimum version. As long as this chain works, your payment system has a skeleton.
mermaid flowchart LR user["User"] frontend["Frontend Page"] backend["Your Backend"] checkout["Stripe Checkout"] webhook["Stripe Webhook"] db["Supabase / Business Database"] user -->|"Click Buy"| frontend frontend -->|"Request checkout session"| backend backend -->|"Create Session with backend price"| checkout frontend -->|"Redirect to payment page"| checkout checkout -->|"Send event after payment"| webhook webhook -->|"Verify signature & update status"| backend backend -->|"Write to orders / subscriptions"| db db -->|"Frontend reads latest status after refresh"| frontend
Translating this into plain language:Translating this into plain language:
If you prefer looking at more formal system diagrams, here's a sequence diagram:If you prefer looking at more formal system diagrams, here's a sequence diagram:
mermaid sequenceDiagram autonumber actor User as User participant Frontend as Frontend Page participant Backend as Backend API participant Stripe as Stripe Checkout User->>Frontend: Click "Upgrade" or "Buy" Frontend->>Backend: POST /api/billing/create-checkout-session Note right of Frontend: Frontend sends plan / userId / email\nDoes NOT send the final charge amount Backend->>Backend: Validate plan and map to priceId Backend->>Stripe: Create Checkout Session Stripe-->>Backend: Return session.url Backend-->>Frontend: Return payment link Frontend-->>User: Redirect to Stripe payment page User->>Stripe: Complete payment
If you want to integrate it into your project as fast as possible, just follow these 5 steps.If you want to integrate it into your project as fast as possible, just follow these 5 steps.
The purpose of this step is not to "just configure something random" -- it's to clearly define in Stripe what you're selling and how you plan to charge for it.The purpose of this step is not to "just configure something random" -- it's to clearly define in Stripe what you're selling and how you plan to charge for it.
In Stripe's model:In Stripe's model:
Pro MembershipProduct represents "what you're selling," for example Pro Membership$9.9/month, $99/yearPrice represents "how much this costs and on what billing cycle," for example $9.9/month, $99/yearWhy do this step first?Why do this step first?
Because later when your backend creates a Checkout Session, you don't pass a raw amount to Stripe -- you pass an existing price_id. Stripe then uses this price_id to generate the actual payment page, amount, currency, and billing cycle.Because later when your backend creates a Checkout Session, you don't pass a raw amount to Stripe -- you pass an existing price_id. Stripe then uses this price_id to generate the actual payment page, amount, currency, and billing cycle.
If you skip this step, the "create payment link" step later won't work at all.If you skip this step, the "create payment link" step later won't work at all.
Many beginners feel annoyed when they see Product and Price, thinking they're learning Stripe's internal jargon. But actually, this step is doing something very straightforward: - Define clearly "what you're selling" - Define clearly "how much it costs" - Let the backend later use a stable price_id to create payment links Once you understand this layer, Checkout Sessions won't feel abstract.Many beginners feel annoyed when they see Product and Price, thinking they're learning Stripe's internal jargon. But actually, this step is doing something very straightforward: - Define clearly "what you're selling" - Define clearly "how much it costs" - Let the backend later use a stable price_id to create payment links Once you understand this layer, Checkout Sessions won't feel abstract.
For a minimum viable subscription system, you need at least these two levels:For a minimum viable subscription system, you need at least these two levels:
ProductOne ProductPrice entriesOne or more Price entriesYou can open these pages directly:You can open these pages directly:
We recommend operating in Test mode first -- don't start building in the live environment.We recommend operating in Test mode first -- don't start building in the live environment.
A typical minimum configuration is:A typical minimum configuration is:
Product: Pro PlanProduct: Pro PlanPrice 1: pro_monthlyPrice 1: pro_monthlyPrice 2: pro_yearlyPrice 2: pro_yearlyWhen operating in the dashboard, follow this order:When operating in the dashboard, follow this order:
Pro PlanFirst create a product Pro PlanAfter completion, you need to note down at least:After completion, you need to note down at least:
price_id for the monthly priceThe price_id for the monthly priceprice_id for the yearly priceThe price_id for the yearly pricepro_monthly, pro_yearlyYour own plan names, e.g. pro_monthly, pro_yearlyIf this is your first time in the Stripe Dashboard, think of it this way:If this is your first time in the Stripe Dashboard, think of it this way:
Product determines what's being sold on the payment pageProduct determines what's being sold on the payment pagePrice determines how much is charged on the payment pagePrice determines how much is charged on the payment pageprice_idWhat the backend will actually use later is mainly price_idThe most important thing on this page is not the product name, but the price_id. Later, whether you're having AI help integrate the backend or troubleshooting issues yourself, what you'll frequently use is: - STRIPE_PRICE_PRO_MONTHLY - STRIPE_PRICE_PRO_YEARLY - The two price_id values they correspond toThe most important thing on this page is not the product name, but the price_id. Later, whether you're having AI help integrate the backend or troubleshooting issues yourself, what you'll frequently use is: - STRIPE_PRICE_PRO_MONTHLY - STRIPE_PRICE_PRO_YEARLY - The two price_id values they correspond to
If you want AI to walk you through the Dashboard configuration first, you can use this prompt:If you want AI to walk you through the Dashboard configuration first, you can use this prompt:
text I'm using Stripe for the first time. Don't modify any code yet -- first help me set up the most basic billing configuration in the Stripe Dashboard. Please give me step-by-step instructions based on these official docs: - https://docs.stripe.com/products-prices/manage-prices - https://docs.stripe.com/checkout/quickstart?lang=node My situation: - I want to build the simplest membership billing - Only two plans: monthly and yearly - I don't understand terms like Product and Price yet Please: 1. First explain in simple terms what Product and Price are. 2. Then guide me step by step: which page to open first -> what to click -> what to fill in. 3. Finally remind me what I need to copy from the Dashboard for the backend to use. 4. If I might make mistakes, please remind me to always stay in test mode.
You typically need at least these environment variables:You typically need at least these environment variables:
STRIPE_SECRET_KEYSTRIPE_SECRET_KEYSTRIPE_WEBHOOK_SECRETSTRIPE_WEBHOOK_SECRETSTRIPE_PRICE_PRO_MONTHLYSTRIPE_PRICE_PRO_MONTHLYSTRIPE_PRICE_PRO_YEARLYSTRIPE_PRICE_PRO_YEARLYAPP_URLAPP_URLSUPABASE_URLSUPABASE_URLSUPABASE_SERVICE_ROLE_KEYSUPABASE_SERVICE_ROLE_KEYYou can open these pages directly:You can open these pages directly:
> โ ๏ธ STRIPE_SECRET_KEY and SUPABASE_SERVICE_ROLE_KEY must only be placed on the backend.> โ ๏ธ STRIPE_SECRET_KEY and SUPABASE_SERVICE_ROLE_KEY must only be placed on the backend.
This step is not about "filling up the .env file" -- it's about placing the most sensitive parts of the payment system on the backend: - Stripe's backend secret key - Webhook signature verification secret - Your own price mapping Simply put: The frontend is only responsible for initiating purchases; the real secrets and pricing logic should stay on the server side.This step is not about "filling up the .env file" -- it's about placing the most sensitive parts of the payment system on the backend: - Stripe's backend secret key - Webhook signature verification secret - Your own price mapping Simply put: The frontend is only responsible for initiating purchases; the real secrets and pricing logic should stay on the server side.
You can also have AI help organize this step:You can also have AI help organize this step:
text Please look at how my project currently stores environment variables, then help me organize the environment variables needed for Stripe. Please refer to these docs: - https://docs.stripe.com/keys - https://docs.stripe.com/webhooks My situation: - I'm a complete beginner - I can't distinguish which variables should go on the frontend vs the backend - I'm not sure whether to edit `.env`, `.env.local`, or another file in the current project Please: 1. First search where environment variables are typically stored in the current project. 2. List the minimum environment variables needed for Stripe integration. 3. Explain in simple terms what each variable does. 4. Tell me which Stripe page to visit to copy each variable. 5. If the project has an example environment variable file, please add the variable names directly.
You don't need to write the API yourself for this step -- just have AI reference the official docs and implement it for you.You don't need to write the API yourself for this step -- just have AI reference the official docs and implement it for you.
First, give it these docs:First, give it these docs:
Then paste this prompt:Then paste this prompt:
text Please look at how my current project's backend code is organized, then help me integrate Stripe payments. Please refer to these official docs: - https://docs.stripe.com/checkout/quickstart?lang=node - https://docs.stripe.com/api/checkout/sessions/create - https://docs.stripe.com/payments/subscriptions My goal is simple: - After the user clicks the buy button, redirect to Stripe's payment page - Only two plans: monthly and yearly - Don't make me decide where to put the code -- look at the project first and place it appropriately Please: 1. First search the project to find the backend entry file, route files, and how environment variables are written. 2. Then reference the official docs to integrate the "create Stripe payment link" step. 3. Don't let me pass the amount myself -- use backend environment variables for pricing. 4. After finishing, tell me which files you changed. 5. Finally, tell me what additional configuration I need to do in the Stripe Dashboard.
The goal of this step is very simple: make the pricing page button call your backend API, then redirect to Stripe Checkout.The goal of this step is very simple: make the pricing page button call your backend API, then redirect to Stripe Checkout.
Reference docs:Reference docs:
Prompt for AI:Prompt for AI:
text Help me connect the "Buy" button in my project to Stripe. Requirements: - Don't change the existing page, only modify the button click logic - After clicking, call the backend API to get the payment link, then redirect to Stripe - If there's an error, show a simple message to the user (e.g. "Payment temporarily unavailable, please try again later") Reference docs: https://docs.stripe.com/payments/checkout/build-integration
This is the most critical step.This is the most critical step.
Many people think "the user paid and was redirected to the success page" means everything is done. No. What matters for your system is: Whether Stripe has officially delivered the event to your Webhook, and whether your backend has successfully updated the database status.Many people think "the user paid and was redirected to the success page" means everything is done. No. What matters for your system is: Whether Stripe has officially delivered the event to your Webhook, and whether your backend has successfully updated the database status.
You can also have AI implement this directly following Stripe's official Webhook docs -- don't write it by hand.You can also have AI implement this directly following Stripe's official Webhook docs -- don't write it by hand.
Reference docs:Reference docs:
Prompt for AI:Prompt for AI:
text Please continue helping me integrate the "automatically activate after successful payment" step with Stripe. Please refer to these official docs: - https://docs.stripe.com/webhooks - https://docs.stripe.com/stripe-cli - https://docs.stripe.com/stripe-cli/use-cli My goal: - After the user pays, don't just redirect to a success page - Actually change the membership status in my database to activated Please: 1. First search the current project for database-related code and how user status is stored. 2. Then add the Stripe webhook. 3. After successful payment, change the corresponding user to active, or update the membership status field currently used in the project. 4. If the project already has subscription tables, order tables, or user tables, prefer to follow the existing structure. 5. After finishing, tell me which files you changed. 6. Also tell me how to test locally whether this step actually works.
If you're using tools like Codex, Claude Code, Trae, or Cursor, you can paste the following prompt directly and have it integrate payments into your project.If you're using tools like Codex, Claude Code, Trae, or Cursor, you can paste the following prompt directly and have it integrate payments into your project.
text Please help me integrate Stripe payments into the current project. I want to build the simplest membership billing feature that works. My requirements: 1. I'm a complete beginner -- please look at the project yourself first, then decide where to modify the code. 2. Don't make me figure out the directory structure, routing structure, or database structure myself. 3. I only want the simplest version first: two plans, monthly and yearly. 4. After clicking buy, the user should be redirected to the Stripe payment page. 5. After successful payment, the membership status in my database should change to activated. 6. Don't add too many complex features upfront, like coupons, upgrades/downgrades, or complex invoicing. Output requirements: 1. First give me a change plan. 2. Then directly modify the code. 3. Finally tell me how to test step by step locally. 4. If any step requires me to do something in the Stripe Dashboard, give me the link and key points directly.
If you want AI to be more tailored to your project, you can also add at the beginning:If you want AI to be more tailored to your project, you can also add at the beginning:
If you want AI to walk you through the entire local integration testing process, you can use this prompt:If you want AI to walk you through the entire local integration testing process, you can use this prompt:
text Please continue helping me get Stripe payments actually working. I want to follow along step by step without guessing. Please refer to the official docs: - https://docs.stripe.com/webhooks - https://docs.stripe.com/stripe-cli - https://docs.stripe.com/stripe-cli/use-cli My goals: 1. Tell me which Stripe pages to open first. 2. Tell me how to get the STRIPE_WEBHOOK_SECRET. 3. Tell me how to use stripe login and stripe listen. 4. Tell me how to verify that checkout.session.completed has successfully reached my local webhook. 5. If the current project needs the frontend and backend running first, tell me the specific commands too. 6. Don't just explain principles -- output actual step-by-step instructions. 7. If I might make a mistake at some step, also tell me what the most common errors look like.
success page as payment successTreating the success page as payment successWhat actually determines the status is the Webhook, not the frontend redirect.What actually determines the status is the Webhook, not the frontend redirect.
This creates a serious price tampering risk.This creates a serious price tampering risk.
express.json()The Webhook route being pre-processed by express.json()Stripe signature verification requires the raw request body.Stripe signature verification requires the raw request body.
Webhooks may be retried. If you add membership or credits on every retry, you'll have problems.Webhooks may be retried. If you add membership or credits on every retry, you'll have problems.
If you just want to get billing working right now:If you just want to get billing working right now:
| Your Primary Users | First Solution to Try |
|---|---|
| International SaaS / Global users | Stripe |
| Mainland China users | Alipay / WeChat Pay |
| Hong Kong or cross-border teams | Stripe + local wallet / FPS aggregation solution |
The specific differences are covered in detail in the appendix.The specific differences are covered in detail in the appendix.
Don't start by thinking "I need to integrate every payment method globally at once." A more practical order is usually: - First pick one primary payment chain based on where your main users are located - Get the minimum viable payment working first - Then add a second or third payment method based on actual user sourcesDon't start by thinking "I need to integrate every payment method globally at once." A more practical order is usually: - First pick one primary payment chain based on where your main users are located - Get the minimum viable payment working first - Then add a second or third payment method based on actual user sources
At this point, you've mastered the most fundamental yet important billing chain:At this point, you've mastered the most fundamental yet important billing chain:
If you just want to quickly integrate payments into your project, the content above is sufficient. The appendix below can be referenced when you actually encounter issues.If you just want to quickly integrate payments into your project, the content above is sufficient. The appendix below can be referenced when you actually encounter issues.
------
When looking at Stripe docs for the first time, it's easy to get confused by these object names. You really only need to understand these:When looking at Stripe docs for the first time, it's easy to get confused by these object names. You really only need to understand these:
| Object | Purpose | What You Can Think of It As |
|---|---|---|
Product | Describes what you're selling | A product or membership plan |
Price | Describes how much it costs and the billing cycle | Monthly, yearly, or one-time purchase |
Checkout Session | Stripe-hosted payment flow | The payment page |
Subscription | Recurring subscription relationship | Auto-renewing membership |
Customer | The paying user | Customer profile in Stripe |
Webhook | Async notification | Stripe telling you "what happened with this payment" |
success Page Does Not Equal Payment SuccessAppendix B: Why the success Page Does Not Equal Payment SuccessMany people think "the user paid and was redirected to the success page" means the payment succeeded. This is the most common pitfall.Many people think "the user paid and was redirected to the success page" means the payment succeeded. This is the most common pitfall.
Imagine you built a membership website:Imagine you built a membership website:
success.htmlPage redirects to your success.htmlWhat's the problem?What's the problem?
The user might not have paid at all, or might have closed the page mid-payment, but can still directly access success.html.The user might not have paid at all, or might have closed the page mid-payment, but can still directly access success.html.
mermaid flowchart TB pay["User completes payment on Stripe"] subgraph unreliable["โ Unreliable path: Only checking the success page"] success["Browser redirects to success page"] fake["Frontend code assumes activated"] risk["Risk: page closed / network disconnected / URL forged / never actually paid"] success --> fake --> risk end subgraph reliable["โ Reliable path: Based on backend Webhook"] event["Stripe server sends Webhook"] verify["Backend verifies signature"] active["Database officially updated to paid"] event --> verify --> active end pay --> success pay --> event
Key differences:Key differences:
| success Page Redirect | Webhook Notification | |
|---|---|---|
| Who initiates it | User's browser | Stripe's server |
| Can it be forged? | Yes, just visit the URL directly | No, there's signature verification |
| Does it guarantee payment success? | Not necessarily | Yes, always |
| How does your system know? | Frontend code guesses | Stripe officially notifies |
mermaid sequenceDiagram autonumber actor User as User participant Frontend as Your Website participant Stripe as Stripe participant Webhook as Your Backend API participant DB as Database User->>Stripe: Complete payment on Stripe page Note over Stripe: Money actually arrives in Stripe account Stripe-->>Frontend: Browser redirects to success page Note over Frontend: โ ๏ธ This is just a redirect<br/>Does not mean the system has confirmed Stripe->>Webhook: Send Webhook notification<br/>"checkout.session.completed" Note over Webhook: โ This is the official notification Webhook->>Webhook: Verify signature<br/>(ensure it's from Stripe, not a hacker) Webhook->>DB: Update user status to "paid" DB-->>Webhook: Save successful Webhook-->>Stripe: Return 200 OK Frontend->>DB: User refreshes page, query status DB-->>Frontend: Return "paid" Note over Frontend: Only now show membership features
Step 1: User pays on StripeStep 1: User pays on Stripe
This is the only moment that confirms "money was actually paid":This is the only moment that confirms "money was actually paid":
Step 2: Browser redirects to the success page (most problematic)Step 2: Browser redirects to the success page (most problematic)
This step is completely unreliable because:This step is completely unreliable because:
yoursite.com/success directly in the browser, accessing it without payingUser can type yoursite.com/success directly in the browser, accessing it without payingStep 3: Stripe sends the WebhookStep 3: Stripe sends the Webhook
This is Stripe proactively notifying your server "this payment has been received":This is Stripe proactively notifying your server "this payment has been received":
Step 4: Backend verifies the signatureStep 4: Backend verifies the signature
Why verify? To prevent hackers from forging notifications.Why verify? To prevent hackers from forging notifications.
Without verification, a hacker could send a fake notification to your server: "User A paid $1000." Your system would then activate membership for the hacker.Without verification, a hacker could send a fake notification to your server: "User A paid $1000." Your system would then activate membership for the hacker.
The verification process:The verification process:
Step 5: Update the databaseStep 5: Update the database
Only after verification passes, update the database:Only after verification passes, update the database:
Step 6: Frontend queries the statusStep 6: Frontend queries the status
The success page should not assume "reaching this page means success." The correct approach:The success page should not assume "reaching this page means success." The correct approach:
javascript // Wrong: Activate directly on the success page // success.html if (window.location.pathname === '/success') { // Dangerous! Anyone can access /success activateMembership(); }
javascript // Correct: Always query the backend on every refresh // success.html async function checkStatus() { const response = await fetch('/api/user/status'); const data = await response.json(); if (data.paymentStatus === 'paid') { showMemberFeatures(); } else { showPendingMessage(); } }
The success page only means "browser redirect succeeded." The Webhook is what means "Stripe has officially confirmed receipt of payment."The success page only means "browser redirect succeeded." The Webhook is what means "Stripe has officially confirmed receipt of payment."
Your system must use the Webhook as the source of truth -- never trust the frontend redirect.Your system must use the Webhook as the source of truth -- never trust the frontend redirect.
| Event | Meaning | What You Typically Do |
|---|---|---|
checkout.session.completed | First subscription activation succeeded | Create a local subscription record |
invoice.paid | Auto-renewal succeeded | Extend the expiration date |
invoice.payment_failed | Auto-charge failed | Mark risk status and notify the user |
customer.subscription.deleted | Subscription canceled | Revoke access or mark as expired |
mermaid stateDiagram-v2 [*] --> NotStarted: User hasn't purchased NotStarted --> Active: checkout.session.completed Active --> Active: invoice.paid Active --> PastDue: invoice.payment_failed PastDue --> Active: User successfully pays outstanding balance Active --> Canceled: customer.subscription.deleted PastDue --> Canceled: Not recovered before expiration Canceled --> [*] state "Not Activated" as NotStarted state "Membership Active" as Active state "Payment Failed / Pending Recovery" as PastDue state "Canceled / Access Revoked" as Canceled
mermaid sequenceDiagram autonumber participant Stripe as Stripe participant Webhook as Your Webhook API participant DB as Subscription / Order Table participant App as Your App actor User as User rect rgb(235, 248, 255) Stripe->>Webhook: invoice.paid Webhook->>DB: Extend current_period_end DB-->>Webhook: Update successful Webhook-->>Stripe: 200 OK App-->>User: Membership remains active end rect rgb(255, 247, 237) Stripe->>Webhook: invoice.payment_failed Webhook->>DB: Mark as past_due DB-->>Webhook: Update successful Webhook-->>Stripe: 200 OK App-->>User: Prompt to update payment method end rect rgb(254, 242, 242) Stripe->>Webhook: customer.subscription.deleted Webhook->>DB: Mark as canceled DB-->>Webhook: Update successful Webhook-->>Stripe: 200 OK App-->>User: Stop premium features end
If your primary users are in mainland China, the first choice is still [Alipay](https://open.alipay.com/) and [WeChat Pay](https://pay.wechatpay.cn/).If your primary users are in mainland China, the first choice is still [Alipay](https://open.alipay.com/) and [WeChat Pay](https://pay.wechatpay.cn/).
Business model:Business model:
Both use a "payment gateway" model. You need to:Both use a "payment gateway" model. You need to:
Technical model:Technical model:
Both follow a "backend creates order + frontend triggers payment + backend receives notification" model, same as Stripe.Both follow a "backend creates order + frontend triggers payment + backend receives notification" model, same as Stripe.
Alipay integration flow:Alipay integration flow:
WeChat Pay integration flow:WeChat Pay integration flow:
Flow: Backend creates order -> gets prepay_id or code_url -> frontend triggers payment -> backend receives notification to confirm successFlow: Backend creates order -> gets prepay_id or code_url -> frontend triggers payment -> backend receives notification to confirm success
Reference links:Reference links:
The Hong Kong market is quite mixed. Common combinations:The Hong Kong market is quite mixed. Common combinations:
Recommended combination:Recommended combination:
Business model: Payment gatewayBusiness model: Payment gateway
Technical model:Technical model:
Best for: International SaaS, indie developers, teams needing flexible customizationBest for: International SaaS, indie developers, teams needing flexible customization
Reference link: https://docs.stripe.com/Reference link: https://docs.stripe.com/
Business model: Payment gatewayBusiness model: Payment gateway
Technical model:Technical model:
Best for: International businesses needing an additional channel, users accustomed to paying with PayPalBest for: International businesses needing an additional channel, users accustomed to paying with PayPal
Reference link: https://developer.paypal.com/docs/Reference link: https://developer.paypal.com/docs/
Business model: Merchant of Record (MoR)Business model: Merchant of Record (MoR)
Technical model:Technical model:
Best for: SaaS teams that don't want to deal with global taxes, especially B2B SaaSBest for: SaaS teams that don't want to deal with global taxes, especially B2B SaaS
Reference link: https://developer.paddle.com/Reference link: https://developer.paddle.com/
Business model: Merchant of Record (MoR)Business model: Merchant of Record (MoR)
Technical model:Technical model:
Best for: Indie developers, digital products, software licensingBest for: Indie developers, digital products, software licensing
Reference link: https://docs.lemonsqueezy.com/Reference link: https://docs.lemonsqueezy.com/
Business model: Payment gateway + global accountsBusiness model: Payment gateway + global accounts
Technical model:Technical model:
Best for: Hong Kong teams, cross-border businesses, companies needing multi-currency accountsBest for: Hong Kong teams, cross-border businesses, companies needing multi-currency accounts
Reference link: https://www.airwallex.com/docs/Reference link: https://www.airwallex.com/docs/
Business model: Payment gatewayBusiness model: Payment gateway
Technical model:Technical model:
Best for: Large enterprises, companies needing omnichannel paymentsBest for: Large enterprises, companies needing omnichannel payments
Reference link: https://docs.adyen.com/Reference link: https://docs.adyen.com/
| Solution | Business Model | Tax Handling | Best For |
|---|---|---|---|
| Stripe | Payment gateway | Handle yourself | International SaaS, developers |
| PayPal | Payment gateway | Handle yourself | International supplementary channel |
| Paddle | MoR | Paddle handles for you | B2B SaaS, don't want to manage taxes |
| Lemon Squeezy | MoR | LS handles for you | Indie developers, digital products |
| Adyen | Payment gateway | Handle yourself | Large enterprises |
| Airwallex | Payment gateway + accounts | Handle yourself | Cross-border businesses, Hong Kong teams |
| Alipay/WeChat | Payment gateway | Handle yourself | Mainland China users |
| Your Market | Recommended Solution |
|---|---|
| Mainland China | Alipay / WeChat Pay |
| Hong Kong | Stripe + Airwallex / Adyen |
| International SaaS | Stripe (manage taxes yourself) or Paddle (MoR handles taxes) |
| International digital products | Stripe / Lemon Squeezy / Paddle |
| Multi-region enterprise | Adyen / Airwallex / Stripe combination |