A React library for integrating Mainstack's external payment system into your applications. This library provides a wrapper around the Mainstack Payments library, making it easy to integrate payment functionality into external applications.
# Using npm
npm install mainstack-external-payments
# Using yarn
yarn add mainstack-external-payments
You can also use the library directly via CDN:
<!-- Load React and ReactDOM from CDN -->
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<!-- Load the Mainstack External Payments library -->
<script src="https://assets.mainstack.co/main/javascript/mainstack-external-payments.umd.js"></script>
import { PaymentLibrary } from 'mainstack-external-payments';
function App() {
const paymentConfig = {
accountId: "YOUR_ACCOUNT_ID",
currency: "USD",
amount: 10,
productId: "YOUR_PRODUCT_ID",
baseUrl: "https://api.mainstack.io",
paymentRedirectUrl: "https://your-website.com/payment-redirect",
cryptoRedirectUrl: "https://your-website.com/crypto-redirect",
metadata: {
mainstack_product_type: "store",
user_id: "YOUR_USER_ID",
product_id: "YOUR_PRODUCT_ID",
account_id: "YOUR_ACCOUNT_ID",
type: "digital_product",
quantity: 1,
productName: "Your Product Name",
},
mainstackProductId: "YOUR_MAINSTACK_PRODUCT_ID",
customizations: {
theme_color: "#6A00FF",
showDefaultInputFields: {
phone: true,
},
},
ip: "0.0.0.0", // This will be replaced with the user's IP
apiKey: "YOUR_API_KEY"
};
return (
<PaymentLibrary
paymentConfig={paymentConfig}
onGoBack={() => console.log("Go back")}
onPaymentComplete={(payload) => console.log("Payment Complete!", payload)}
/>
);
}
<div id="payment-container"></div>
<script type="text/babel">
// Access the PaymentLibrary component from the global MainstackExternalPayments object
const { PaymentLibrary } = MainstackExternalPayments;
// Payment configuration
const paymentConfig = {
accountId: "YOUR_ACCOUNT_ID",
currency: "USD",
amount: 10,
productId: "YOUR_PRODUCT_ID",
baseUrl: "https://api.mainstack.io",
paymentRedirectUrl: "https://your-website.com/payment-redirect",
cryptoRedirectUrl: "https://your-website.com/crypto-redirect",
metadata: {
mainstack_product_type: "store",
user_id: "YOUR_USER_ID",
product_id: "YOUR_PRODUCT_ID",
account_id: "YOUR_ACCOUNT_ID",
type: "digital_product",
quantity: 1,
productName: "Your Product Name",
},
mainstackProductId: "YOUR_MAINSTACK_PRODUCT_ID",
customizations: {
theme_color: "#6A00FF",
showDefaultInputFields: {
phone: true,
},
},
ip: "0.0.0.0", // This will be replaced with the user's IP
apiKey: "YOUR_API_KEY"
};
// Render the PaymentLibrary component
const App = () => {
return (
<PaymentLibrary
paymentConfig={paymentConfig}
onGoBack={() => console.log("Go back")}
onPaymentComplete={(payload) => console.log("Payment Complete!", payload)}
/>
);
};
// Render the App component to the DOM
ReactDOM.createRoot(document.getElementById('payment-container')).render(<App />);
</script>
Prop | Type | Required | Description |
---|---|---|---|
paymentConfig | object | Yes | Configuration for the payment (see details below) |
summaryTitle | string | ReactNode | No | Custom title for the payment summary section |
checkoutTitle | string | ReactNode | No | Custom title for the checkout page |
defaultFormValues | object | No | Default values for the payment form fields (fullname, email, phone) |
contactForm | ReactNode | No | Custom contact form component to replace the default form |
onGoBack | function | No | Callback when the user clicks the back button |
onInitializePayment | function | No | Callback when payment is initialized. It contains an argument that has the values for the default form inputs, and must return a promise that resolves an object with a terminate boolean property. The payment initialization will terminate immediately the promise resolves depending on what the value of terminate is. |
onPaymentComplete | function | Yes | Callback when payment is completed, receives payment result payload |
The paymentConfig
object requires the following properties:
Property | Type | Required | Description |
---|---|---|---|
accountId | string | Yes | The Mainstack account ID |
currency | string | Yes | The currency code (e.g., "USD", "NGN", "GBP") |
amount | number | Yes | The payment amount |
productId | string | Yes | The ID of the product being purchased |
baseUrl | string | Yes | The base URL for API requests (e.g., "https://api.mainstack.io") |
paymentRedirectUrl | string | Yes | URL to redirect after payment completion (see Payment Redirect Page section below) |
cryptoRedirectUrl | string | No | URL to redirect after crypto payment completion (should be the same as paymentRedirectUrl) |
metadata | object | Yes | Additional information about the transaction (see below) |
mainstackProductId | string | Yes | The Mainstack product ID |
customizations | object | No | UI customization options (see below) |
ip | string | No | User's IP address (will be auto-detected if not provided) |
apiKey | string | Yes | Your Mainstack API key |
discountAmount | number | No | Amount to discount from the total |
paymentOptions | array | No | Available payment methods (wallet, startbutton, stripe, paystack, crypto) |
The metadata
object requires the following properties:
Property | Type | Required | Description |
---|---|---|---|
reference | string | No | A unique reference for the transaction |
type | string | Yes | Type of product (e.g., "digital_product") |
account_id | string | Yes | The account ID (same as accountId) |
user_id | string | Yes | The user ID making the purchase |
product_id | string | Yes | The product ID (same as productId) |
mainstack_product_type | string | Yes | The Mainstack product type (store, editor, bookings, mediakit, invoicing, hosted_courses) |
productName | string | Yes | The name of the product |
Additional custom fields can be added to the metadata object as needed.
The customizations
object allows you to customize the appearance of the payment form:
Property | Type | Description |
---|---|---|
theme_color | string | Primary color for buttons and UI elements (hex code) |
button_label | string | Custom label for the payment button |
font_family | string | Custom font family for the payment form |
hideDetails | boolean | Whether to hide payment details |
showDefaultInputFields | object | Configure which input fields to show (email, phone, fullname) |
hideBackButton | boolean | Whether to hide the back button |
padding | string | Custom padding for the payment form |
isSingleColumn | boolean | Whether to use a single column layout |
The defaultFormValues
object allows you to pre-fill the payment form:
Property | Type | Description |
---|---|---|
fullname | string | Pre-filled full name |
string | Pre-filled email address | |
phone | string | Pre-filled phone number |
The paymentRedirectUrl
should point to a page that handles the payment completion notification. This page should contain specific code to communicate the payment completion back to the parent window or application.
For React applications, your redirect page should look like this:
import { useEffect } from "react";
const PostMessagePage = () => {
useEffect(() => {
if (window.self !== window.top) {
// If the page is in an iframe, send message to parent
(() => {
window.parent.postMessage({ type: "Payment Completed" }, "*");
})();
} else {
// If the page is in a new tab/window
(() => {
const channel = new BroadcastChannel("app-channel");
channel.postMessage({
type: "Payment Completed",
message: "Payment tab is closed",
});
window.close();
})();
}
}, []);
return <div></div>;
};
export default PostMessagePage;
For HTML-only applications, your redirect page should look like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Payment Redirect</title>
</head>
<body>
<div id="payment-complete-message">Payment Completed</div>
<script>
// Execute when the page loads
document.addEventListener('DOMContentLoaded', function() {
if (window.self !== window.top) {
// If the page is in an iframe, send message to parent
window.parent.postMessage({ type: "Payment Completed" }, "*");
} else {
// If the page is in a new tab/window
const channel = new BroadcastChannel("app-channel");
channel.postMessage({
type: "Payment Completed",
message: "Payment tab is closed",
});
// Attempt to close the window
window.close();
}
});
</script>
</body>
</html>
- The library first verifies the API configuration by making a request to
${baseUrl}/api/payment-config?product_id=${productId}
with the provided API key. - It fetches the user's IP information if not already stored in localStorage.
- It renders the payment form with the configured options.
- When the user completes the payment, they are redirected to the
paymentRedirectUrl
orcryptoRedirectUrl
if the payment method requires it. - The redirect page sends a message back to the parent application.
- The parent application receives the message and triggers the
onPaymentComplete
callback. - If the paymentMethod doesn't require calling
paymentRedirectUrl
theonPaymentComplete
callback is triggered immediately.
The Mainstack External Payments library supports the following payment methods:
- Credit/Debit Cards (via Stripe)
- Mainstack Wallet
- Crypto
- Bank Transfers
- Mobile Money
The available payment methods are determined by the account settings and can be further restricted using the paymentOptions
array.
The library provides built-in error handling for common payment issues. If an error occurs during the API configuration verification, an error message will be displayed to the user.
# Install dependencies
yarn install
# Start development server
yarn dev
# Build for production
yarn build
# Test CDN build
yarn test-cdn
MIT