npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zencemarketing/storepay-sdk

v0.1.0-alpha.1

Published

StorePay React SDK to open StorePay payment gateway UI in an iframe modal

Downloads

187

Readme

@zencemarketing/storepay-sdk

StorePay React SDK - A simple React SDK to open StorePay payment gateway UI in an iframe modal.

Installation

npm install @zencemarketing/storepay-sdk

Quick Start

import React, { useEffect, useState } from 'react';
import { StorePayProvider, useStorePay, StorePayButton } from '@zencemarketing/storepay-sdk';

function App() {
  return (
    <StorePayProvider>
      <PaymentComponent />
    </StorePayProvider>
  );
}

function PaymentComponent() {
  const { init, openPayment, isInitialized } = useStorePay();
  const [accessToken, setAccessToken] = useState<string | null>(null);

  // Your API configuration
  const tokenUrl = 'YOUR_TOKEN_URL';
  const clientId = 'YOUR_CLIENT_ID';
  const clientSecret = 'YOUR_CLIENT_SECRET';
  const paymentGatewayUrl = 'YOUR_PAYMENT_GATEWAY_URL';
  const apiBaseUrl = 'YOUR_API_BASE_URL';

  useEffect(() => {
    // Initialize SDK
    try {
      init({
        paymentGatewayUrl: paymentGatewayUrl
      });
      console.log('SDK initialized successfully');
    } catch (error) {
      console.error('SDK initialization failed:', error);
    }
  }, [init]);

  // Format bill date time helper
  const formatBillDateTime = () => {
    const now = new Date();
    const day = now.getDate().toString().padStart(2, '0');
    const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
    const month = monthNames[now.getMonth()];
    const year = now.getFullYear();
    const hours = now.getHours().toString().padStart(2, '0');
    const minutes = now.getMinutes().toString().padStart(2, '0');
    const seconds = now.getSeconds().toString().padStart(2, '0');
    return `${day}-${month}-${year} ${hours}:${minutes}:${seconds}`;
  };

  // Get access token
  const getAccessToken = async () => {
    try {
      const data = `Client_Id=${encodeURIComponent(clientId)}&Client_Secret=${encodeURIComponent(clientSecret)}&Grant_Type=client_credentials&Scope=api.storepay.com`;
      
      const response = await fetch(`${tokenUrl}/connect/token`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
        },
        body: data
      });

      if (!response.ok) {
        throw new Error(`Token request failed: ${response.status} ${response.statusText}`);
      }

      const responseData = await response.json();
      return responseData.access_token;
    } catch (error) {
      console.error('Failed to get access token:', error);
      throw error;
    }
  };

  // Generate payment iframe URL (complete flow)
  const generatePaymentIframeUrl = async (paymentData: any) => {
    try {
      // Step 1: Get access token (use cached or fetch new)
      let token = accessToken;
      if (!token) {
        token = await getAccessToken();
        setAccessToken(token || null);
      }

      // Step 2: Call GeneratePaymentLink API
      const response = await fetch(`${apiBaseUrl}/api/GeneratePaymentLink`, {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(paymentData)
      });

      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        throw new Error(`Payment failed: ${errorData.message || response.statusText}`);
      }

      const responseData = await response.json();

      // Check for error in response
      if (responseData.returnCode !== "0" && responseData.returnCode !== 0) {
        throw new Error(responseData.returnMessage || "Payment link generation failed");
      }

      // Extract tokenId from response
      const tokenId = responseData.tokenId;
      
      if (!tokenId) {
        throw new Error("Token ID not received from API");
      }

      // Get programCode from paymentData
      const programCode = paymentData.programCode;
      if (!programCode) {
        throw new Error("Program code is required in payment data");
      }

      // Step 3: Encrypt the URL parameters using AESEncrypt API
      const textToEncrypt = `programcode=${programCode}&tokenId=${tokenId}&language=en`;
      
      const encryptResponse = await fetch(`${apiBaseUrl}/api/AESEncrypt`, {
        method: 'POST',
        headers: {
          'accept': 'text/plain',
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${token}`
        },
        body: JSON.stringify({ text: textToEncrypt })
      });

      if (!encryptResponse.ok) {
        throw new Error(`Encryption failed: ${encryptResponse.statusText}`);
      }

      // Get encrypted text from JSON response
      const encryptResponseData = await encryptResponse.json();
      const encryptedText = encryptResponseData.encryptedText;
      
      if (!encryptedText) {
        throw new Error("Encrypted text not received from API");
      }

      // Step 4: Build the iframe URL
      const iframeUrl = `${paymentGatewayUrl}/ol/?${encryptedText}`;
      return iframeUrl;
    } catch (error) {
      // If token expired, try once more with new token
      if (error instanceof Error && error.message.includes('401')) {
        const newToken = await getAccessToken();
        setAccessToken(newToken || null);
        throw new Error("Token expired. Please try again.");
      }
      throw error;
    }
  };

  // Handle payment
  const handlePayment = async () => {
    try {
      // Prepare payment data
      const paymentData = {
        programCode: 'YOUR_PROGRAM_CODE',
        storeCode: 'YOUR_STORE_CODE',
        billDateTime: formatBillDateTime(),
        terminalId: 'YOUR_TERMINAL_ID',
        merchantTxnID: `TXN_${Date.now()}`,
        name: "Customer Name",
        email: null,
        mobile: '1234567890',
        customerId: 'YOUR_CUSTOMER_ID',
        amount: 100,
        mode: '1',
        linkRequired: true
      };

      // Generate iframe URL (all API calls happen here)
      const iframeUrl = await generatePaymentIframeUrl(paymentData);

      // Open payment using SDK
      openPayment(iframeUrl, {
        onSuccess: (data) => {
          console.log('Payment Success:', data);
          alert(`Payment Successful!\nTransaction ID: ${data.merchantTxnId}\nMessage: ${data.message}`);
        },
        onFailure: (data) => {
          console.log('Payment Failed:', data);
          alert(`Payment Failed!\nBill ID: ${data.billId || 'N/A'}`);
        },
        onCancel: (data) => {
          console.log('Payment Cancelled:', data);
          alert(`Payment Cancelled: ${data.message}`);
        },
        onError: (data) => {
          console.log('Payment Error:', data);
          alert(`Payment Error: ${data.message}`);
        },
        onTimeout: (data) => {
          console.log('Payment Timeout:', data);
          alert(`Payment Timed Out!\nMessage: ${data.message}`);
        }
      });
    } catch (error) {
      console.error('Payment failed:', error);
      alert(error instanceof Error ? error.message : 'Payment failed');
    }
  };

  return (
    <button onClick={handlePayment} disabled={!isInitialized}>
      Pay with StorePay
    </button>
  );
}

Using StorePayButton Component

import { StorePayButton } from '@zencemarketing/storepay-sdk';

function PaymentButton() {
  const [iframeUrl, setIframeUrl] = useState<string | null>(null);

  const generatePaymentUrl = async () => {
    // Generate iframe URL using your API calls
    const url = await generatePaymentIframeUrl(paymentData);
    setIframeUrl(url);
  };

  return (
    <>
      <button onClick={generatePaymentUrl}>Generate Payment URL</button>
      {iframeUrl && (
        <StorePayButton
          iframeUrl={iframeUrl}
          callbacks={{
            onSuccess: (data) => console.log('Success:', data),
            onFailure: (data) => console.log('Failed:', data),
            onCancel: (data) => console.log('Cancelled:', data),
            onError: (data) => console.log('Error:', data),
            onTimeout: (data) => console.log('Timeout:', data)
          }}
        >
          Pay with StorePay
        </StorePayButton>
      )}
    </>
  );
}

Features

  • ✅ Simple iframe modal integration
  • ✅ Payment status callbacks (success, failure, cancel, error, timeout)
  • ✅ No API calls in SDK - full control in your application
  • ✅ TypeScript support
  • ✅ React 16.8+ compatible
  • ✅ Token caching support
  • ✅ Error handling

API Reference

init(config: StorePayInitConfig)

Initialize the SDK with payment gateway URL.

init({
  paymentGatewayUrl: 'YOUR_PAYMENT_GATEWAY_URL'
});

openPayment(iframeUrl: string, callbacks?: StorePayPaymentCallbacks)

Open payment modal with the generated iframe URL.

openPayment(iframeUrl, {
  onSuccess: (data) => { /* ... */ },
  onFailure: (data) => { /* ... */ },
  onCancel: (data) => { /* ... */ },
  onError: (data) => { /* ... */ },
  onTimeout: (data) => { /* ... */ }
});

closeCheckout()

Close the payment modal programmatically.

isInitialized

Boolean indicating if SDK is initialized.

Payment Flow

  1. Get Access Token - Call your token API endpoint
  2. Generate Payment Link - Call GeneratePaymentLink API with payment data
  3. Encrypt URL - Call AESEncrypt API to encrypt payment parameters
  4. Build Iframe URL - Combine payment gateway URL with encrypted text
  5. Open Payment - Use SDK's openPayment() to open the modal

Documentation

See README-SDK.md for complete documentation and API reference.

Version

Current Version: 0.1.0-alpha.1 (Pre-release)

This is a pre-release version. Official release coming soon.

License

MIT

Author

ZenceMarketing