@zencemarketing/storepay-sdk
v0.1.0-alpha.4
Published
StorePay React SDK to open StorePay payment gateway UI in an iframe modal
Readme
StorePay Web SDK (React + Vanilla JS)
This SDK provides a checkout modal + iframe wrapper for StorePay’s externally hosted payment UI.
- In React, you use a provider + hook (
StorePayProvider,useStorePay) or a convenience component (StorePayButton). - In vanilla JS, you use a plain JS instance (
storePay/createStorePay).
The SDK is responsible for:
- Rendering a modal overlay and loading a StorePay checkout URL inside an
<iframe>. - Listening to
window.postMessageevents from that iframe and mapping them into callbacks.
The SDK does not call StorePay APIs like GeneratePaymentLink, AESEncrypt, or token endpoints. Your application (or backend) must generate the final checkout URL (iframeUrl) and then pass it to the SDK.
Installation
NPM (recommended)
npm i @zencemarketing/storepay-sdkLocal path (for testing before publish)
npm i /absolute/path/to/ZenceStorePayPackage entrypoints
- React entry:
@zencemarketing/storepay-sdk - Vanilla entry:
@zencemarketing/storepay-sdk/vanilla
Browser build (script tag): dist/vanilla.iife.js exposes a global StorePayVanilla.
React usage
1) Wrap your app
import { StorePayProvider } from "@zencemarketing/storepay-sdk";
export default function App() {
return (
<StorePayProvider>
{/* your app */}
</StorePayProvider>
);
}2) Initialize once
import { useEffect } from "react";
import { useStorePay } from "@zencemarketing/storepay-sdk";
export function BootstrapStorePay() {
const { init } = useStorePay();
useEffect(() => {
init({
paymentGatewayUrl: "https://example.storepay.live"
});
}, [init]);
return null;
}3) Open checkout (you provide the iframeUrl)
import { useStorePay } from "@zencemarketing/storepay-sdk";
export function PayButton() {
const { openPayment, isInitialized } = useStorePay();
return (
<button
disabled={!isInitialized}
onClick={() => {
const iframeUrl = "https://example.storepay.live/ol/?<encryptedParams>";
openPayment(iframeUrl, {
onSuccess: (data) => console.log("SUCCESS", data),
onFailure: (data) => console.log("FAILED", data),
onCancel: (data) => console.log("CANCELLED", data),
onTimeout: (data) => console.log("TIMEOUT", data),
onError: (data) => console.log("ERROR", data)
});
}}
>
Pay with StorePay
</button>
);
}Using StorePayButton
import { StorePayButton } from "@zencemarketing/storepay-sdk";
export function PayWithComponent() {
return (
<StorePayButton
iframeUrl="https://example.storepay.live/ol/?<encryptedParams>"
callbacks={{
onSuccess: (data) => console.log("SUCCESS", data)
}}
>
Pay with StorePay
</StorePayButton>
);
}Vanilla JS usage
Option A: Bundler (ESM/CJS)
import { storePay } from "@zencemarketing/storepay-sdk/vanilla";
storePay.init({ paymentGatewayUrl: "https://example.storepay.live" });
document.getElementById("payBtn").addEventListener("click", () => {
const iframeUrl = "https://example.storepay.live/ol/?<encryptedParams>";
storePay.openPayment(iframeUrl, {
onSuccess: (data) => console.log("SUCCESS", data),
onFailure: (data) => console.log("FAILED", data)
});
});If you want multiple isolated instances:
import { createStorePay } from "@zencemarketing/storepay-sdk/vanilla";
const sdk = createStorePay();
sdk.init({ paymentGatewayUrl: "https://example.storepay.live" });Option B: Script tag (no bundler)
Use the browser bundle dist/vanilla.iife.js (from your own hosting/CDN after publish). It exposes a global StorePayVanilla.
<script src="/path/to/node_modules/@zencemarketing/storepay-sdk/dist/vanilla.iife.js"></script>
<script>
const sdk = StorePayVanilla.storePay;
sdk.init({ paymentGatewayUrl: "https://example.storepay.live" });
// sdk.openPayment(iframeUrl, callbacks)
</script>How to generate iframeUrl (your responsibility)
New Process API Flow (Recommended)
The recommended flow uses the onlinepayment/Process API to get a direct payment URL:
- Obtain access token (recommended: do this on your backend)
POST /api/GeneratePaymentLinkwith payment payload → gettokenIdandstorepayRefNum- Generate SHA-512 hash using:
key|amount|firstname|email||||||salt POST /onlinepayment/Processwith payment details → get direct payment URL- Use the returned URL directly in
openPayment()
Step 1: Generate SHA-512 Hash
const generateSHA512Hash = async ({ amount, firstName, email }) => {
const key = 'your_key'; // Provided by StorePay
const salt = 'your_salt'; // Provided by StorePay
// Format amount to 4 decimal places
const formattedAmount = amount.toFixed(4);
// Build input string: key|amount|firstname|email||||||salt
const inputString = `${key}|${formattedAmount}|${firstName}|${email}||||||${salt}`;
// Encode and hash
const msgBuffer = new TextEncoder().encode(inputString);
const hashBuffer = await crypto.subtle.digest('SHA-512', msgBuffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('').toLowerCase();
};Step 2: Call Process API
const processUrl = 'https://paymentapi-uat.storepay.live/onlinepayment/Process';
// Base64 encode success/failure URLs
const successUrl = btoa('https://yourdomain.com/paymentsuccess');
const failureUrl = btoa('https://yourdomain.com/paymentfailure');
const processRequestObj = {
ProgramCode: 'your_program_code',
StoreCode: 'your_store_code',
StorepayRefNum: storepayRefNum, // From GeneratePaymentLink response
Mobile: 'customer_mobile',
Amount: 100.00,
Hash: paymentHash, // Generated SHA-512 hash
furl: failureUrl, // Base64 encoded failure URL
surl: successUrl, // Base64 encoded success URL
Language: 'en',
IsDiscountApplied: false,
UDF1: merchantTxnID, // Merchant transaction ID
UDF2: customerId,
UDF3: 'YourBrandName',
UDF4: customerId,
UDF5: null,
name: 'Customer Name',
shoppingBagNumber: null,
isSDKcall: true
};
const processPayload = new URLSearchParams();
processPayload.append('request', JSON.stringify(processRequestObj));
const processResponse = await fetch(processUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: processPayload.toString()
});
const responseData = await processResponse.json();
const paymentUrl = responseData.data; // Direct payment URLStep 3: Domain Replacement (Optional)
Replace the payment domain with your configured gateway URL:
let iframeUrl = paymentUrl;
if (paymentUrl.includes('https://payment-domain.storepay.live')) {
iframeUrl = paymentUrl.replace('https://payment-domain.storepay.live', paymentGatewayUrl);
}
You can see a working reference implementation in `sample-test.tsx` (React) and the minimal vanilla demo in the repository root `index.html`.
---
## Payment result events (postMessage contract)
The hosted checkout (iframe) must send messages to the parent page using `window.postMessage` with a payload like:
```js
{ type: "STOREPAY_PAYMENT_SUCCESS", data: { ... } }Supported type values:
STOREPAY_PAYMENT_SUCCESS→ callsonSuccess(data)and closes the modalSTOREPAY_PAYMENT_FAILED→ callsonFailure(data)and closes the modalSTOREPAY_PAYMENT_CANCELLED→ callsonCancel(data)and closes the modalSTOREPAY_PAYMENT_TIMEOUT→ callsonTimeout(data)and closes the modalSTOREPAY_PAYMENT_ERROR→ callsonError(data)and keeps the modal open
API reference
Shared types
StorePayInitConfig
paymentGatewayUrl: string
Used mainly for optional origin validation of postMessage events.
StorePayPaymentCallbacks
onSuccess?: (data) => voidonFailure?: (data) => voidonCancel?: (data) => voidonError?: (data) => voidonTimeout?: (data) => void
React API (@zencemarketing/storepay-sdk)
StorePayProvider(component): provides SDK state and renders the modaluseStorePay()returns:init(config)openPayment(iframeUrl, callbacks?)closeCheckout()isInitialized
StorePayButton(component): convenience wrapper aroundopenPayment
Vanilla API (@zencemarketing/storepay-sdk/vanilla)
storePay(singleton)createStorePay()(factory)
Instance methods:
init(config)openPayment(iframeUrl, callbacks?)closeCheckout()destroy()(removesmessagelistener + closes modal)isInitialized()
Security notes (recommended)
For production, validate the origin of postMessage events.
The SDK currently includes an origin check commented out in both React and vanilla builds. You should enable an origin check that matches your hosted checkout domain (e.g. https://example.storepay.live).
Build / local development
From the ZenceStorePay directory:
npm i
npm run buildArtifacts are generated in dist/:
- React:
dist/index.esm.js,dist/index.cjs.js,dist/index.d.ts - Vanilla:
dist/vanilla.esm.js,dist/vanilla.cjs.js,dist/vanilla.d.ts,dist/vanilla.iife.js
