Adding a Payment Gateway to Your Online Store: What the Website Must Do in 2026
Adding a payment gateway to your online store means more than pasting a key into a settings screen. The website must create the order on your server, hand the customer to a payment screen, bring them back to a clear result page, confirm payment through a server-to-server webhook rather than trusting the browser, recover gracefully from failures, show the policy pages a gateway expects, and be tested end to end in test mode before real money moves. This is the build-side checklist for store owners and the developers they hire.
It leaves out fees, tax and settlement questions, and it does not rank gateways. Providers are named only as examples whose public documentation was read in October 2026 and is linked at the end. Docs change, so treat them as the final word.
Start with the decision that shapes everything: where does the payment screen live?
There are three broad patterns, and the rest of your build follows from the choice.
Hosted checkout (redirect). The customer leaves your site, pays on a page run by the provider, and is sent back. Shopify's help centre describes this split: "direct" providers let customers pay inside your store, while "external" providers send them to a third-party hosted page. Stripe's documentation likewise describes a hosted Checkout page that returns the customer to a URL you configure.
Embedded or overlay checkout. Your page loads the provider's script and shows a payment form or modal. Razorpay's Standard Checkout is this style, with a handler function that runs in the browser when the customer finishes.
Fully custom fields. You build the payment fields yourself with the provider's components or API. This gives the most design control and the most responsibility for security, accessibility and testing.
For most small and mid-sized stores, start with hosted or the provider's standard embedded checkout: less sensitive data touches your pages and there is less UI to test. Choose custom fields only for a specific design need, and agree who owns the security review. Check your provider's current guidance on card-security obligations for your integration type before committing.
Whichever you pick, one rule never changes: the browser is not the source of truth about whether a payment succeeded. That rule is the heart of the webhook section below.
Integration paths by platform
Shopify: native checkout versus third-party providers
On Shopify, checkout is Shopify's, and you choose which payment providers appear in it from the Payments page in the admin. Shopify's help centre says it supports over 100 third-party card providers and that merchants can combine them with Shopify Payments.
- You usually do not write payment code. Your work is configuration, testing, and the surrounding experience: confirmation emails, thank-you page, order status.
- With an external provider the customer visibly leaves and returns. Test that return on mobile, where "did my order go through?" support emails cluster.
- Custom logic must fit what Shopify's checkout currently permits. Check Shopify's developer documentation before promising a design change, because checkout customisation rules have changed over the years.
WooCommerce: a gateway plugin, or your own
On WooCommerce a gateway is normally a plugin. WooCommerce's developer documentation explains that gateways extend the WC_Payment_Gateway class and implement process_payment(), which updates the order and returns a result and redirect URL; for asynchronous confirmation it points to WooCommerce API callback endpoints.
- Use the provider's official plugin where one exists. A custom gateway is justified only when no plugin fits your provider or flow.
- Confirm Checkout block compatibility. The block-checkout documentation says a gateway needs client-side registration with
registerPaymentMethod()and a server-side class extendingAbstractPaymentMethodType. A plugin built only for the classic checkout may not appear in the block checkout. Ask the vendor, then test your real page. - Test status transitions. The documentation describes
payment_complete()for confirmed payments andupdate_status()for pending ones. Orders should move to processing only after confirmation. Our guide on keeping webhooks, orders and refunds in sync covers status drift in depth. - Test with your real plugin stack, since caching and security plugins can interfere with callbacks.
Custom stores: the API integration
On a custom store you own three server-side pieces: creating the payment attempt, verifying the result, and handling events. Razorpay's guide says an order should be created for every payment through its Orders API, server-side, and that you must verify the payment signature on your server before fulfilling the order. Stripe's hosted flow is shaped the same way: create a session on the server, send the customer to pay, receive an event, fulfil once.
A sound integration has these properties:
- The amount comes from your database, never from a value the browser sends.
- Secret keys live only in server configuration, never in front-end code or a public repository.
- A local order record exists before the customer is sent to pay, with a status such as "awaiting payment".
- Only the verified confirmation path may mark the order paid.
For donation sites the structure is the same; see our article on building an NGO donation checkout with webhooks, receipts and testing.
The return page and the webhook: why you need both
This is the section developers most often shortcut, so it gets the most space.
When a customer finishes paying, two different things happen. The browser is redirected, or a JavaScript handler fires, back on your site. Separately, the provider's server can send a notification to a URL on your server, which is the webhook. These are not interchangeable.
Stripe's fulfilment documentation is blunt about it: you can't rely on triggering fulfilment only from the landing page, because customers are not guaranteed to visit it. Someone can pay successfully and then lose their connection before your page loads. Stripe's guidance is to use webhooks to make sure fulfilment happens for every payment, and also to trigger the same fulfilment function from the landing page so the customer sees an immediate result. Razorpay's webhook documentation says not to use callback_url as a substitute for webhooks to track payment status on your server, and recommends relying on webhooks for automation, with an API fetch if a user-facing flow needs the status immediately and the webhook has not arrived yet.
What the return page should do
- Read the order or session identifier from the URL, then ask your own server (or the provider's API) for the real status. Never trust a "success=true" parameter in the URL.
- Show one of three honest states: paid, failed, or still confirming. "Still confirming" is a legitimate state; give it a friendly message and a short automatic refresh instead of an error.
- Never show "order confirmed" purely because the customer reached the page.
- Make the page safe to reload and safe to open twice.
What the webhook handler must do
From the Stripe and Razorpay documentation, the consistent requirements are:
Verify the signature. Stripe signs events with a
Stripe-Signatureheader and a signing secret, and its documentation warns that frameworks which alter the raw request body will break verification. Read your provider's page for the exact header and algorithm, and verify against the raw body.Respond quickly with a 2xx. Stripe says to return success before doing complex work and to process events through an asynchronous queue.
Be idempotent. Stripe notes the same event may arrive more than once and its fulfilment function may run concurrently, so act once per payment: store the provider's event or payment ID under a unique database constraint and check it before changing the order.
Do not depend on event order. Stripe states that events may arrive out of order. If you receive "refunded" before "paid", your code should fetch the current object from the API rather than guess.
Expect retries, and keep a manual re-sync. Stripe documents automatic retries over several days in live mode; you still want a way to re-check an order against the provider.
A useful mental model: the webhook is the bank statement, the return page is the receipt the customer holds. The receipt is nice to have, but you ship against the statement.
Payment-failure and retry UX
Payments fail for ordinary reasons: a wrong OTP, a bank timeout, a declined card, a closed tab, a UPI app that never returned control. The website cannot prevent all of it, but it decides whether a failure costs you the sale.
- Keep the cart and the order. A failed payment should land the customer back at the payment step with items, address and contact details intact.
- Say what happened in plain words. For example: "The payment did not go through. If money was debited, your bank will normally return it; see our refund policy." Do not promise timelines you do not control. TODO: confirm refund wording with your provider.
- Offer "Try again" and "Pay another way" as separate visible actions.
- Distinguish failed from pending. Stripe's docs note some methods have delayed success notification. Showing "failed" and letting the customer pay twice is worse than showing "confirming".
- Prevent double orders. Disable the pay button after the first click, tie each attempt to one order ID, and surface a second successful payment for an already-paid order to a human in your admin.
- Give a human fallback, such as a visible WhatsApp number or email, to calm the "was I charged?" worry. For the form side of checkout, see our piece on designing a checkout form and guest checkout that Indian shoppers finish.
Trust signals and the pages your site needs
Customers decide whether to pay in seconds, and gateways review your site before switching you to live mode. Razorpay's account-activation documentation lists what it expects: a shipping policy, contact page, pricing details, terms and conditions, privacy policy, cancellation and refunds policy, and an About Us link, and says that without these details you cannot access live-mode API keys. A web search also surfaced guidance that the contact page should carry business name, address, phone and email and that refund policies should give concrete timeframes; that is secondary information, so confirm it on the provider's page. Other providers have their own criteria, so ask for theirs in writing.
| Page | What it should contain | Build note |
|---|---|---|
| Terms and conditions | Who you are, how orders are accepted | Footer link and near the pay button |
| Privacy policy | What checkout collects, who processes it, how to ask for deletion | Must match what the site really does |
| Cancellation and refunds | Who can cancel, how, stated timeframes | Use numbers you can keep |
| Shipping or delivery | The general process, written by operations | Keep factual |
| Contact us | Business name, address, phone, email | Real details, working form |
| About us | Who runs the business | Helps reviewers and customers |
On the checkout page itself: use HTTPS everywhere with no insecure assets, show accepted payment methods near the pay button using the provider's official logos, and make the total and delivery charges visible before the customer taps pay. Do not display security badges or certifications you have not earned.
Mobile and UPI-intent checkout: a design view
Much Indian store traffic arrives on phones, so the payment step should be designed for a thumb and an app switch. This section covers design only. UPI on mobile typically hands control to a UPI app and returns; which flows (app intent, QR, UPI ID) are available depends on your provider and the customer's device, so confirm in the provider's docs and test on real phones. TODO: confirm intent-flow availability in the chosen provider's current docs.
- Prepare for the app switch. Your page may be suspended or reloaded while the customer is in another app. The pending order must survive, which is another reason it exists on the server before payment begins.
- Tell people what to do. "Approve the request in your UPI app, then come back to this page", plus a visible "I've paid, check status" button that calls your status endpoint.
- Handle the return. The customer may come back in a different tab or in-app browser, so look the order up from a signed identifier rather than relying on an existing session.
- Use full-width, comfortable tap targets, labelled fields and visible error messages, and test on older phones and slow connections.
The test-mode plan
Every major provider offers a test or sandbox mode. Razorpay's documentation structures the work as build, test, then a go-live checklist, and Stripe recommends testing with sandbox events and its CLI before going live. Treat testing as a project phase with written cases.
Setup. Keep test and live keys in separate environment settings so live keys cannot reach staging by accident. Register a separate test webhook endpoint; Stripe notes signing secrets differ between test and live. For local work, use a tunnelling tool or the provider's CLI to receive webhooks.
Cases to write down and run
- Successful payment: order paid, stock reduced once, confirmation email sent once.
- Failed payment for each provider test scenario: cart preserved, clear message, retry works.
- Customer closes the tab after paying: the webhook alone marks the order paid.
- Webhook delivered twice: no duplicate action.
- Webhook arriving before, and after, the return page.
- Bad signature: rejected, nothing changes, event logged.
- Slow webhook: return page shows "confirming" and then updates.
- Amount tampering in browser tools: the server ignores it.
- Refund from the provider dashboard: order status updates.
- Mobile pass on Android and iOS, two browsers, and a social-app in-app browser.
Record expected and actual results; the sheet becomes your regression list.
Go-live checklist
Switch to live mode only when every line is true.
- Provider account approved and live keys issued; Razorpay says website details are required first.
- All policy pages published, linked from the footer and consistent with real practice.
- Live keys only in server configuration; test keys removed from production.
- Live webhook endpoint registered over valid HTTPS, signing secret set, and only the needed events enabled.
- One real low-value payment by the team, then refunded, to confirm the production path. Check your provider allows this.
- Staff know what to do when a customer says "money debited but no order".
- Rate limiting or bot protection on checkout endpoints, so card-testing bots cannot hammer order creation.
Monitoring after launch
- Log every webhook with event ID, type, order reference, result and time. Stripe's dashboard shows delivery status per event; keep your own logs too.
- Alert on signature failures, repeated 4xx or 5xx from your webhook route, and orders stuck in "awaiting payment" past a set window.
- Run a daily reconciliation check comparing unpaid orders against successful provider payments and the reverse. It is the safety net behind the webhook.
- Update the gateway plugin or SDK in staging first, rerun the test cases, then promote, and rotate secrets when staff leave or a leak is suspected.
Working with Govindani Infotech
Govindani Infotech Pvt. Ltd. is a Pune web design and development company that builds e-commerce stores and NGO donation sites. If you are planning a store, or your current checkout sometimes loses orders or confuses customers, we can review the payment flow with you: platform, return and webhook handling, failure screens, policy pages and a written test plan. Our e-commerce development service covers Shopify and WooCommerce builds, and our checkout system service focuses on the checkout journey itself. For stores that need smoother order messaging after payment, see our WhatsApp Business API options.
You can reach us on WhatsApp or through the contact page. We are at 2nd Floor, Landmark Plaza, 206 Satara Road, Parvati Paytha, Pune 411009. Send us your platform, your provider and what goes wrong today, and we will tell you honestly whether it needs a fix or a rebuild.
Frequently Asked Questions
Should I use hosted checkout or an embedded payment form?
For most small stores, hosted or the provider's standard embedded checkout is the safer start, because less sensitive handling happens on your pages and there is less UI to test. Choose a fully custom form only for a specific design need and with a clear owner for security and testing.
Is the redirect back to my site enough to confirm a payment?
No. Customers can pay and then lose connection or close the tab before your return page loads, so provider documentation from Stripe and Razorpay recommends confirming payments through server-side webhooks. Use the return page to show the status, and let the verified webhook or a server-side API check mark the order paid.
Which policy pages does my store need before going live?
Razorpay's activation documentation lists a shipping policy, contact page, pricing details, terms and conditions, privacy policy, cancellation and refunds policy, and an About Us link. Requirements differ by provider, so ask yours for its current checklist and make sure the pages match how your business really operates.
How do I handle failed or pending payments without losing the sale?
Keep the cart and order details, explain the result in plain words, and offer both a retry and a different payment method. Show pending payments as "confirming" rather than failed, and block duplicate attempts for the same order.
How long should testing take before launch?
It depends on the platform and the payment methods, so we do not quote a standard duration. Run written cases for success, failure, duplicate and delayed webhooks, tampering and mobile devices, and go live only when each passes.
Sources:
- Stripe, Receive Stripe events in your webhook endpoint: https://docs.stripe.com/webhooks
- Stripe, Fulfill orders (Checkout): https://docs.stripe.com/checkout/fulfillment
- Razorpay, Webhooks: https://razorpay.com/docs/webhooks/
- Razorpay, Web Standard Checkout integration: https://razorpay.com/docs/payments/payment-gateway/web-integration/standard/
- Razorpay, Business website details (activation requirements): https://razorpay.com/docs/payments/dashboard/account-settings/business-website-details/
- Shopify Help Center, Third-party payment providers: https://help.shopify.com/en/manual/payments/third-party-providers
- WooCommerce Developer Docs, Payment Gateway API: https://developer.woocommerce.com/docs/features/payments/payment-gateway-api
- WooCommerce Developer Docs, Checkout block payment method integration: https://developer.woocommerce.com/docs/block-development/cart-and-checkout-blocks/checkout-payment-methods/payment-method-integration/