IME Pay
Needs keysIME Pay web checkout. The server gets a token that binds the amount, the user is redirected, then Confirm or Recheck settles it.
Environment
What this test shop reads. Values are never shown. Your own app can pass the same values to
Config however it likes.
IMEPAY_MERCHANT_CODErequired
Merchant code
Missing
IMEPAY_MODULErequired
Module name (sent base64 in the Module header)
Missing
IMEPAY_API_USERrequired
Basic-auth user
Missing
IMEPAY_API_PASSWORDrequired
Basic-auth password
Missing
IMEPAY_MERCHANT_CODE= IMEPAY_MODULE= IMEPAY_API_USER= IMEPAY_API_PASSWORD=
Integrate in your Go app
The same five steps work for every provider — only the constructor changes.
-
Install
go get github.com/mukezhz/pay-np
-
Create the provider
Build it once at startup and keep it as a
paynp.Provider. Load secrets from your config, never the browser.import ( paynp "github.com/mukezhz/pay-np" "github.com/mukezhz/pay-np/imepay" ) p, err := imepay.New(imepay.Config{ MerchantCode: code, Module: module, APIUser: user, APIPassword: pass, }) if err != nil { log.Fatal(err) } var provider paynp.Provider = p -
Start a payment
Generate a unique TxnID per attempt and store it with the amount and
co.ProviderRef— IME Pay's Lookup needs it .co.Writeredirects or renders the auto-submit form.func pay(w http.ResponseWriter, r *http.Request) { order := loadOrder(r) // your amount, never the browser's txnID := newTxnID() // unique per attempt, e.g. "ord-42-a1" ret := "https://shop.example/return/" + txnID co, err := provider.Initiate(r.Context(), paynp.InitiateRequest{ TxnID: txnID, Amount: order.Amount, // paynp.Paisa: Rs 1,500 = 150000 Description: order.Title, SuccessURL: ret, FailureURL: ret + "?failed=1", }) if err != nil { http.Error(w, "payment unavailable", http.StatusBadGateway) return } saveAttempt(txnID, order.ID, order.Amount, co.ProviderRef) co.Write(w, r) } -
Handle the return, then Lookup
The redirect is only a hint and is unsigned. Fulfil only when Lookup says
SUCCESS; it already checks the amount.func paymentReturn(w http.ResponseWriter, r *http.Request) { _ = r.ParseForm() cb, _ := provider.ParseCallback(r.Form) // may fail on cancel; that's fine a := loadAttempt(r.PathValue("txn")) // amount comes from YOUR records tx, err := provider.Lookup(r.Context(), paynp.LookupRequest{ TxnID: a.TxnID, Amount: a.Amount, ProviderRef: a.Ref, Callback: cb, }) switch { case errors.Is(err, paynp.ErrAmountMismatch): flagForReview(a) // never fulfil case err != nil: // provider unreachable: leave pending, the reconciler retries case tx.Status == paynp.StatusSuccess: fulfilOnce(a.OrderID, tx.ProviderRef) // idempotent case tx.Status.Final(): markFailed(a, tx.Status) } http.Redirect(w, r, "/orders/"+a.OrderID, http.StatusSeeOther) } -
Reconcile pending payments
Users close the tab after paying and IME Pay sends no webhook. Re-run Lookup on attempts that are not final.
for range time.Tick(time.Minute) { for _, a := range pendingAttempts(olderThan(2 * time.Minute)) { tx, err := provider.Lookup(ctx, paynp.LookupRequest{ TxnID: a.TxnID, Amount: a.Amount, ProviderRef: a.Ref, }) if err == nil && tx.Status == paynp.StatusSuccess { fulfilOnce(a.OrderID, tx.ProviderRef) } else if err == nil && tx.Status.Final() { markFailed(a, tx.Status) } } }
Mobile apps (Android, iOS, Flutter)
Apps go through your backend: create the payment, open the checkout in an in-app browser, and let the provider return to your server, which runs Lookup and redirects to the app.
In-app browser via checkout_url; the token in provider_ref is what Lookup rechecks.
POST http://localhost:8080/api/payments
{"provider": "imepay", "amount": "100.00", "app_return_url": "paynp://payment-done"}
→ 201 {"txn_id": "np-…", "status": "PENDING", "provider_ref": "…",
"checkout_url": "http://localhost:8080/pay/np-…"}
1. Open checkout_url in Custom Tabs / ASWebAuthenticationSession.
2. The provider returns to http://localhost:8080/return/imepay/np-…; the server runs Lookup
and redirects to paynp://payment-done?txn_id=np-…&status=SUCCESS.
3. Confirm before fulfilling: GET http://localhost:8080/api/payments/np-…
→ {"status": "SUCCESS", "final": true}Notes
- Lookup calls Confirm when the callback carried a TransactionId, otherwise Recheck by token.