ConnectIPS
Needs keysNCHL ConnectIPS. The browser form-POSTs a request signed with your merchant RSA key; status comes from gettxndetail.
Environment
What this test shop reads. Values are never shown. Your own app can pass the same values to
Config however it likes.
CONNECTIPS_MERCHANT_IDrequired
Numeric merchant ID
Missing
CONNECTIPS_APP_IDrequired
Application ID
Missing
CONNECTIPS_APP_NAMErequired
Application name
Missing
CONNECTIPS_PASSWORDrequired
Status API Basic-auth password
Missing
CONNECTIPS_PFX_PATHrequired
Path to the merchant .pfx certificate
Missing
CONNECTIPS_PFX_PASSWORD
Password of the .pfx file
Optional
CONNECTIPS_USERNAME
Basic-auth user (defaults to APP_ID)
Optional
CONNECTIPS_HOST
Override uat.connectips.com / login.connectips.com
Optional
CONNECTIPS_MERCHANT_ID= CONNECTIPS_APP_ID= CONNECTIPS_APP_NAME= CONNECTIPS_PASSWORD= CONNECTIPS_PFX_PATH=
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/connectips" ) key, err := connectips.ParsePFX(pfxBytes, pfxPassword) p, err := connectips.New(connectips.Config{ MerchantID: "550", AppID: "MER-550-APP-1", AppName: "Shop", Password: pw, PrivateKey: key, }) 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.
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. ConnectIPS returns to the URL registered with NCHL with?TXNID=, so take the TxnID from the callback.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 ConnectIPS 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 only. NCHL returns to the URL registered with it, then the server redirects to the app.
POST http://localhost:8080/api/payments
{"provider": "connectips", "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/connectips/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
- Return URLs are registered with NCHL, not sent per request. Register {BASE_URL}/return/connectips.
- TxnID must be ≤ 20 characters; amounts are sent in paisa.
- Confirm in UAT that gettxndetail reports txnAmt in paisa.