Article summary
A gateway is not just a pay button. Orders, stock, receipts, and refunds must land in your admin panel.
Zarinpal is not just a pay button. Orders, stock, callbacks, receipts, and refunds must land in your panel so accounting and support share one story. This guide collects what to lock before you sell online, from KGSM Laravel implementations.
This guide — Connecting Zarinpal to Laravel — before you sell online — is written from KGSM delivery work: scoped first versions, staged releases, and support after handoff.
Audience, risk, and how KGSM works
A shop or product owner wiring Zarinpal to Laravel who fears incomplete orders and double charges
A gateway without an order model means bad stock, lost receipts, and a customer charged twice.
KGSM closes the order, callback, and finance panel before the pay button, and tests the connection in stages on staging.
Order model first, gateway second
Before any SDK, the order table needs a code, a locked amount, shipping, status, and line items with reserved stock. If the amount comes from the live browser basket, a user with devtools changes the price. Reserve stock when the order is created and release it if payment misses the window; otherwise two people buy the same unit. Keep statuses few and meaningful: awaiting payment, paid, failed, expired, fulfilling. The panel should show that status without guesswork. KGSM’s first version usually closes this model in web development and the storefront in web design. Stable SKUs matter for accounting; a display title is not enough. Store tax and discount on the same order so profit reports are not rebuilt from memory. Do not take the Zarinpal merchant to production until this works on staging with one real SKU.
Verify the stored order amount, not the browser basket. A shop trusted the form amount; a buyer cheapened the order by editing a field.
Callbacks, verify, and idempotency
Payment is final when server-side verify succeeds, not when the browser hits a thank-you page. Callbacks can arrive twice, late, or after the user closes the tab, so the handler must be idempotent: the same authority or gateway id may mark an order paid only once. Verify against the stored amount, not the form amount. If verify fails, mark failed and release the reservation by your rule. Keep the raw gateway response; Zarinpal support without that log is guessing. Queue post-payment work — email, invoice, final stock decrement — so a slow mailbox does not run the transaction twice. Do not confuse sandbox with live amounts, but test success, cancel, and timeout. KGSM shows this path in a staged demo with two rapid payment clicks so double-inserts surface early.
The callback arrived twice and two invoices were issued for one authority. Make the callback idempotent so two clicks do not create two payments.
Amount security, merchant keys, and the return URL
Merchant credentials live only in production .env, never in the repo. Make the return page unguessable so nobody marks someone else’s order paid by editing the query string. HTTPS is mandatory; a gateway on HTTP also burns trust with the bank. Reject zero or negative amounts in order validation. If you have several gateways, store the gateway name on the same receipt so bank mismatches are traceable. Support should view receipts but not edit a paid order’s amount. For product-page SEO, stock and price must come from the same panel source; ranking an out-of-stock product with a pay button drives bounce. If the app also sells, give mobile apps the same order API so a second gateway is not built on a second basket.
Lock stock with a reservation window and release after expiry. There was no stock reservation and two customers bought the last unit.
Refunds, mismatches, and the next morning
Selling online without a cancel and refund path creates endless tickets. Put refund status on the order and keep the gateway transaction id beside it so accounting does not wire twice by hand. Daily mismatch: paid rows in your system versus Zarinpal settlement need one report; close the gap the same day, not at month end. Paid orders with no items, or items with no payment, are two health queries that should blink on the admin home. Send SMS after verify, not after the button click. KGSM trains the panel with one test order, one failure, and one refund so week-one support is not stuck. Maintenance means SDK updates, watching gateway API changes, and a backup before a campaign. To go live, send a SKU and shipping scenario via contact so the estimate is not “just a Zarinpal button”.
The thank-you page emailed before verify; the payment had failed and support was lost. Keep the raw gateway log for support.
Do not share merchant keys across the repo and staging. A Friday campaign without a mismatch report ended Sunday with ten payments and no orders.
The refund existed only in the Zarinpal panel; accounting wired the customer again. Turn on a daily mismatch report before any ad campaign.
Send the success SMS after verify, not after the click. A shop trusted the form amount; a buyer cheapened the order by editing a field.
The callback arrived twice and two invoices were issued for one authority. Model refunds as order status, not a silent manual wire.
Implementation checklist
- Verify the stored order amount, not the browser basket.
- Make the callback idempotent so two clicks do not create two payments.
- Lock stock with a reservation window and release after expiry.
- Keep the raw gateway log for support.
- Do not share merchant keys across the repo and staging.
- Turn on a daily mismatch report before any ad campaign.
- Send the success SMS after verify, not after the click.
- Model refunds as order status, not a silent manual wire.
Field scenario 1
A shop trusted the form amount; a buyer cheapened the order by editing a field.
Verify the stored order amount, not the browser basket.
Field scenario 2
The callback arrived twice and two invoices were issued for one authority.
Make the callback idempotent so two clicks do not create two payments.
Field scenario 3
There was no stock reservation and two customers bought the last unit.
Lock stock with a reservation window and release after expiry.
Field scenario 4
The thank-you page emailed before verify; the payment had failed and support was lost.
Keep the raw gateway log for support.
Field scenario 5
A Friday campaign without a mismatch report ended Sunday with ten payments and no orders.
Do not share merchant keys across the repo and staging.
Field scenario 6
The refund existed only in the Zarinpal panel; accounting wired the customer again.
Turn on a daily mismatch report before any ad campaign.
Related KGSM pages
Continue with KGSM services: طراحی سایت, برنامهنویسی و توسعه نرمافزار. For a scoped estimate, use the contact form.
When you apply this on a live team, write it as an operating rule, not a slide: Verify the stored order amount, not the browser basket. Then assign an owner and a review date so the rule survives the first busy week.
When you apply this on a live team, write it as an operating rule, not a slide: Make the callback idempotent so two clicks do not create two payments. Then assign an owner and a review date so the rule survives the first busy week.
When you apply this on a live team, write it as an operating rule, not a slide: Lock stock with a reservation window and release after expiry. Then assign an owner and a review date so the rule survives the first busy week.
When you apply this on a live team, write it as an operating rule, not a slide: Keep the raw gateway log for support. Then assign an owner and a review date so the rule survives the first busy week.
When you apply this on a live team, write it as an operating rule, not a slide: Do not share merchant keys across the repo and staging. Then assign an owner and a review date so the rule survives the first busy week.
When you apply this on a live team, write it as an operating rule, not a slide: Turn on a daily mismatch report before any ad campaign. Then assign an owner and a review date so the rule survives the first busy week.
When you apply this on a live team, write it as an operating rule, not a slide: Send the success SMS after verify, not after the click. Then assign an owner and a review date so the rule survives the first busy week.
When you apply this on a live team, write it as an operating rule, not a slide: Model refunds as order status, not a silent manual wire. Then assign an owner and a review date so the rule survives the first busy week.
When you apply this on a live team, write it as an operating rule, not a slide: A shop trusted the form amount; a buyer cheapened the order by editing a field. Then assign an owner and a review date so the rule survives the first busy week.
Frequently asked questions
What must be ready before wiring Zarinpal? +
Why is server-side verify mandatory? +
How do you stop double charges? +
Do we need in-site refunds? +
Does KGSM build the whole shop? +
Keywords
Related KGSM services