@arto-pay/js-sdk
v1.0.1
Published
Arto Pay JavaScript SDK
Maintainers
Readme
@arto-pay/js-sdk
A lightweight JavaScript SDK for integrating Ojire Payment Gateway into your web applications. This SDK provides a simple modal-based payment flow that works with any JavaScript framework or vanilla JS.
Features
- Modal-based payment UI
- Support for multiple payment methods (QRIS, Virtual Account, Credit Card)
- Sandbox and Production environments
- TypeScript support
- Framework agnostic (works with React, Vue, Angular, or vanilla JS)
- Lightweight (~5KB gzipped)
Installation
NPM
npm install @arto-pay/js-sdkYarn
yarn add @arto-pay/js-sdkCDN (Script Tag)
<script
src="https://cdn.jsdelivr.net/npm/@arto-pay/js-sdk@latest/dist/arto-pay-sdk.umd.js"
data-client-key="pk_your_public_key_here"
data-sandbox="true"
></script>Quick Start
1. Get Your API Keys
Obtain your API keys from the Ojire Dashboard:
- Public Key (
pk_...): Used in frontend to identify your merchant - Secret Key (
sk_...): Used in backend to create payment intents (never expose this in frontend!)
2. Create Payment Intent (Backend)
First, create a payment intent from your backend server:
// Backend (Node.js example)
const response = await fetch('/v1.1/payment-intents', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Secret-Key': 'sk_your_secret_key'
},
body: JSON.stringify({
amount: "3850000",
currency: "IDR",
customerId: "customer_123",
description: "Order from Brutal Aquatics",
metadata: {
"orderId": "order_1778833554484",
"items": [
"HALFMOON BETTA [BLOOD RED]",
"GALAXY KOI PLAKAT",
"BLACK SAMURAI FIGHTER"
]
}
})
});
const paymentIntent = await response.json();
// Returns: { id, clientSecret, customerToken, ... }Response
{
"amount": "3850000.00",
"secret": "pi_97c367e7-ac2e-472e-bc56-XXXXXXXXXXXX",
"createdAt": "2026-05-15T08:25:54.701513Z",
"currency": "IDR",
"customerToken": "ct_feb48b9e-20dd-406f-a1da-XXXXXXXXXXX",
"id": "feb48b9e-xxxx-xxxxx-xxxx-fc41f0025ad2",
"metadata": {
"items": [
"HALFMOON BETTA [BLOOD RED]",
"GALAXY KOI PLAKAT",
"BLACK SAMURAI FIGHTER"
],
"orderId": "order_1778833554484"
},
"status": "created"
}3. Open Payment Modal (Frontend)
Using Script Tag (Vanilla JS)
<!DOCTYPE html>
<html>
<head>
<title>Checkout</title>
</head>
<body>
<button id="pay-button">Pay Now</button>
<script
src="https://cdn.jsdelivr.net/npm/@arto-pay/js-sdk@latest/dist/arto-pay-sdk.umd.js"
data-client-key="pk_your_public_key"
data-sandbox="true"
></script>
<script>
document.getElementById('pay-button').addEventListener('click', function() {
// paymentIntent should come from your backend
ArtoPay.openPayment({
token: paymentIntent.customerToken,
clientSecret: paymentIntent.secret,
paymentId: paymentIntent.id,
orderId: 'order_12345',
onSuccess: function(result) {
console.log('Payment successful!', result);
// Redirect to success page or update UI
},
onPending: function(result) {
console.log('Payment pending', result);
// Show pending message
},
onError: function(result) {
console.log('Payment failed', result);
// Show error message
}
});
});
</script>
</body>
</html>Using ES Modules
import ArtoPay from '@arto-pay/js-sdk';
// Configure SDK
ArtoPay.configure({
sandbox: true // Set to false for production
});
// Open payment modal
ArtoPay.openPayment({
token: paymentIntent.customerToken,
clientSecret: paymentIntent.secret,
paymentId: paymentIntent.id,
orderId: 'order_12345',
onSuccess: (result) => {
console.log('Payment successful!', result);
},
onPending: (result) => {
console.log('Payment pending', result);
},
onError: (result) => {
console.log('Payment failed', result);
}
});API Reference
ArtoPay.openPayment(options)
Opens the payment modal.
Options
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| token | string | No | Customer token from payment intent |
| secret | string | Yes | Client secret from payment intent |
| paymentId | string | Yes | Payment intent ID |
| orderId | string | Yes | Order ID for tracking |
| sandbox | boolean | No | Override sandbox mode for this payment |
| onSuccess | function | No | Callback when payment succeeds |
| onPending | function | No | Callback when payment is pending |
| onError | function | No | Callback when payment fails |
Example
ArtoPay.openPayment({
token: 'cust_token_xxx',
secret: 'pi_secret_xxx',
paymentId: 'pi_xxx',
orderId: 'order_12345',
sandbox: true,
onSuccess: (result) => {
console.log('Transaction status:', result.transactionStatus);
console.log('Mapped status:', result.status);
console.log('Raw data:', result.rawData);
},
onPending: (result) => {
// Handle pending payment (e.g., waiting for bank transfer)
},
onError: (result) => {
// Handle failed payment
}
});ArtoPay.closePayment()
Programmatically closes the payment modal.
ArtoPay.closePayment();ArtoPay.isPaymentOpen()
Returns true if the payment modal is currently open.
if (ArtoPay.isPaymentOpen()) {
console.log('Modal is open');
}ArtoPay.configure(config)
Configures SDK settings.
ArtoPay.configure({
sandbox: false // Set to true for sandbox, false for production
});ArtoPay.isSandbox()
Returns true if SDK is in sandbox mode.
if (ArtoPay.isSandbox()) {
console.log('Running in sandbox mode');
}Callback Result Object
All callbacks receive a PaymentResult object:
interface PaymentResult {
transactionStatus: TransactionStatus;
status: 'success' | 'pending' | 'error';
rawData: Record<string, unknown>;
}Transaction Status Values
| Status | Mapped To | Description |
|--------|-----------|-------------|
| settlement | success | Payment completed |
| capture | success | Payment captured |
| pending | pending | Waiting for payment |
| challenge | pending | Needs verification |
| deny | error | Payment denied |
| cancel | error | Payment cancelled |
| expire | error | Payment expired |
| failure | error | Payment failed |
React Integration
import React, { useState } from 'react';
import ArtoPay from '@arto-pay/js-sdk';
import type { PaymentResult } from '@arto-pay/js-sdk';
function CheckoutButton() {
const [loading, setLoading] = useState(false);
const [status, setStatus] = useState<string>('');
const handlePayment = async () => {
setLoading(true);
try {
// Get payment intent from your backend
const response = await fetch('/v1.1/payment-intents', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ amount: 100000 })
});
const paymentIntent = await response.json();
ArtoPay.openPayment({
token: paymentIntent.customerToken,
clientSecret: paymentIntent.clientSecret,
paymentId: paymentIntent.id,
orderId: 'order_12345',
onSuccess: (result: PaymentResult) => {
setLoading(false);
setStatus('Payment successful!');
// Redirect or update UI
},
onPending: (result: PaymentResult) => {
setLoading(false);
setStatus('Payment pending. Please complete the payment.');
},
onError: (result: PaymentResult) => {
setLoading(false);
setStatus(`Payment failed: ${result.transactionStatus}`);
}
});
} catch (error) {
setLoading(false);
setStatus('Failed to initialize payment');
}
};
return (
<div>
<button onClick={handlePayment} disabled={loading}>
{loading ? 'Processing...' : 'Pay Now'}
</button>
{status && <p>{status}</p>}
</div>
);
}
export default CheckoutButton;Vue Integration
<template>
<div>
<button @click="handlePayment" :disabled="loading">
{{ loading ? 'Processing...' : 'Pay Now' }}
</button>
<p v-if="status">{{ status }}</p>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import ArtoPay from '@arto-pay/js-sdk';
import type { PaymentResult } from '@arto-pay/js-sdk';
const loading = ref(false);
const status = ref('');
const handlePayment = async () => {
loading.value = true;
try {
const response = await fetch('/v1.1/payment-intents', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ amount: 100000 })
});
const paymentIntent = await response.json();
ArtoPay.openPayment({
token: paymentIntent.customerToken,
clientSecret: paymentIntent.clientSecret,
paymentId: paymentIntent.id,
orderId: 'order_12345',
onSuccess: (result: PaymentResult) => {
loading.value = false;
status.value = 'Payment successful!';
},
onPending: (result: PaymentResult) => {
loading.value = false;
status.value = 'Payment pending';
},
onError: (result: PaymentResult) => {
loading.value = false;
status.value = `Payment failed: ${result.transactionStatus}`;
}
});
} catch (error) {
loading.value = false;
status.value = 'Failed to initialize payment';
}
};
</script>Configuration
Script Tag Attributes
When using the CDN version, configure via data attributes:
<script
src="https://cdn.jsdelivr.net/npm/@arto-pay/js-sdk@latest/dist/arto-pay-sdk.umd.js"
data-client-key="pk_your_public_key"
data-sandbox="true"
></script>| Attribute | Description |
|-----------|-------------|
| data-client-key | Your public key (required) |
| data-sandbox | Set to "true" for sandbox, "false" for production |
Programmatic Configuration
import ArtoPay from '@arto-pay/js-sdk';
ArtoPay.configure({
sandbox: process.env.NODE_ENV !== 'production'
});Environments
| Environment | API URL |
|-------------|---------|
| Sandbox | https://api-sandbox.arto-pay.com |
| Production | https://api.arto-pay.com |
TypeScript Support
The SDK includes TypeScript definitions. Import types as needed:
import ArtoPay, {
PaymentResult,
PaymentCallbacks,
OpenPaymentOptions,
TransactionStatus,
PaymentResultStatus
} from '@arto-pay/js-sdk';Error Handling
try {
ArtoPay.openPayment({
token: paymentIntent.customerToken,
clientSecret: paymentIntent.secret,
paymentId: paymentIntent.id,
orderId: 'order_12345',
onError: (result) => {
switch (result.transactionStatus) {
case 'expire':
alert('Payment expired. Please try again.');
break;
case 'cancel':
alert('Payment was cancelled.');
break;
case 'deny':
alert('Payment was denied. Please use a different payment method.');
break;
default:
alert('Payment failed. Please try again.');
}
}
});
} catch (error) {
// Handle SDK initialization errors
console.error('SDK Error:', error.message);
}Security Best Practices
- Never expose your Secret Key in frontend code
- Always create payment intents on your backend
- Validate payment status on your backend after receiving callbacks
- Use HTTPS in production
- Verify webhook signatures for server-to-server notifications
Browser Support
- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
- Mobile browsers (iOS Safari, Chrome for Android)
Changelog
v1.0.0
- Initial release
- Modal-based payment flow
- Support for QRIS, Virtual Account, and Credit Card
- Sandbox and Production environments
- TypeScript definitions
v1.0.1
- Fixing README.md documentation
Support
- Documentation: https://docs.arto-pay.com
License
MIT License - see LICENSE for details.
