verifyet-sdk
v1.2.2
Published
Verify Et JavaScript SDK for payment verification
Maintainers
Readme
Verify Et SDK
JavaScript SDK for integrating Verify Et payment verification into your website.
NPM: verifyet-sdk
CDN: https://unpkg.com/verifyet-sdk/dist/verifyet.min.js
API Docs: https://api.verifyet.com/docs
API Base URL: https://api.verifyet.com/api/v1
Installation
NPM
npm install verifyet-sdkCDN
<script src="https://unpkg.com/verifyet-sdk/dist/verifyet.min.js"></script>Quick Start
Checkout Flow (Recommended)
The checkout flow shows users your payment details, waits for them to pay, then verifies:
import { VerifyEt } from 'verifyet-sdk';
const verifyEt = new VerifyEt({
apiKey: 'pk_live_your_key_here'
});
// Create checkout and redirect user
document.getElementById('pay-button').addEventListener('click', async () => {
await verifyEt.checkout({
amount: 1500, // Amount in ETB
service: 'cbe', // 'cbe' or 'telebirr' (optional)
returnUrl: 'https://yoursite.com/success',
cancelUrl: 'https://yoursite.com/cancel'
});
});Verification Flow (For Already-Paid Transactions)
If the user already made a payment and you just need to verify:
import { VerifyEt } from 'verifyet-sdk';
const verifyEt = new VerifyEt({
apiKey: 'pk_live_your_key_here'
});
// Open verification modal
document.getElementById('verify-button').addEventListener('click', async () => {
try {
const result = await verifyEt.verify({
mode: 'modal',
amount: 1500, // Optional: verify specific amount
onSuccess: async (result) => {
// Send token to your server for confirmation
const response = await fetch('/api/confirm-payment', {
method: 'POST',
body: JSON.stringify({ token: result.token })
});
if (response.ok) {
window.location.href = '/thank-you';
}
},
onError: (error) => {
console.error('Verification failed:', error.message);
}
});
} catch (error) {
console.error('Verification cancelled or failed');
}
});Flows Explained
1. Checkout Flow (Pay-Then-Verify)
Best for: E-commerce, subscriptions, services
┌─────────────────────────────────────────────────────────────┐
│ 1. User clicks "Pay Now" │
│ 2. SDK creates checkout session │
│ 3. User redirected to verifyet.com/checkout │
│ - Sees your account number & name │
│ - Sees amount to pay │
│ - Countdown timer │
│ 4. User makes payment via their bank app │
│ 5. User scans receipt on checkout page │
│ 6. We verify payment went to YOUR account │
│ 7. User redirected to your returnUrl with token │
│ 8. Your server confirms token │
└─────────────────────────────────────────────────────────────┘2. Verification Flow (Already-Paid)
Best for: Invoice payments, manual transfers, remittances
┌─────────────────────────────────────────────────────────────┐
│ 1. User already made a payment │
│ 2. User clicks "Verify Payment" │
│ 3. Modal opens with QR scanner / manual entry │
│ 4. User scans their receipt │
│ 5. We verify transaction exists │
│ 6. Your onSuccess callback receives token │
│ 7. Your server confirms token │
└─────────────────────────────────────────────────────────────┘Configuration
Initialize Options
import { VerifyEt } from 'verifyet-sdk';
const verifyEt = new VerifyEt({
// Required
apiKey: 'pk_live_...',
// Optional
baseUrl: 'https://api.verifyet.com/api/v1', // Override API URL
locale: 'en', // 'en' | 'am' (Amharic)
theme: 'dark', // 'dark' | 'light'
debug: false, // Enable console logging
});Checkout Options
await verifyEt.checkout({
// Required
amount: 1500, // Amount in ETB
// Optional
service: 'cbe', // 'cbe' | 'telebirr' | undefined (user choice)
orderReference: 'ORD-123', // Your order ID
description: 'Product X', // What they're paying for
expiresInMinutes: 15, // Session timeout (default: 15)
returnUrl: 'https://...', // Success redirect
cancelUrl: 'https://...', // Cancel redirect
metadata: { orderId: 123 }, // Custom data (returned in webhook)
mode: 'redirect', // 'redirect' | 'popup' | 'modal'
// Modal mode callbacks (only used when mode: 'modal')
onSuccess: (result) => {}, // { checkoutId, token, status }
onCancel: () => {}, // User closed modal
onError: (err) => {}, // { code, message }
});Checkout Display Modes
| Mode | Description | Use Case |
|------|-------------|----------|
| redirect | Full page redirect to checkout (default) | Standard web checkout |
| popup | Opens in new browser window | Desktop apps |
| modal | Iframe overlay on your page | SPA apps, better UX |
Important: Modal mode requires
allowed_originsto be configured during client registration. Only domains listed inallowed_originscan embed checkout in an iframe. This is a security feature to prevent unauthorized embedding.
Modal Mode Example:
await verifyEt.checkout({
amount: 1500,
mode: 'modal',
onSuccess: (result) => {
console.log('Payment verified!', result.token);
// Send token to your server to confirm
},
onCancel: () => {
console.log('User cancelled checkout');
},
onError: (error) => {
console.error('Checkout error:', error.message);
}
});Verify Options
await verifyEt.verify({
// Optional
mode: 'modal', // 'modal' | 'redirect' | 'inline'
service: 'cbe', // Pre-select service
amount: 1500, // Expected amount
theme: 'dark', // Override global theme
locale: 'en', // Override global locale
returnUrl: 'https://...', // For redirect mode
cancelUrl: 'https://...', // For redirect mode
metadata: { ... }, // Custom data
// Override global callbacks
onSuccess: (result) => {},
onCancel: () => {},
onError: (error) => {},
});Handling Results
Success Redirect (Checkout Flow)
After successful checkout, user is redirected to your returnUrl:
https://yoursite.com/success?checkout_id=chk_abc123&token=eyJ...&status=verifiedParse Redirect Parameters
// On your success page
const params = new URLSearchParams(window.location.search);
const checkoutId = params.get('checkout_id');
const token = params.get('token');
const status = params.get('status');
if (status === 'verified' && token) {
// Confirm on your server
confirmPayment(token);
}Server-Side Confirmation (Required!)
Always confirm tokens on your server before fulfilling orders!
Node.js Example
const fetch = require('node-fetch');
async function confirmPayment(token) {
const response = await fetch('https://api.verifyet.com/api/v1/verifications/confirm', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.VERIFYET_SECRET_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ token }),
});
const data = await response.json();
if (data.success) {
console.log('Payment verified!');
console.log('Amount:', data.verification.amount);
console.log('Payer:', data.verification.payer_name);
console.log('Transaction ID:', data.verification.transaction_id);
return data.verification;
} else {
throw new Error(data.error);
}
}Python Example
import requests
import os
def confirm_payment(token):
response = requests.post(
'https://api.verifyet.com/api/v1/verifications/confirm',
headers={
'Authorization': f'Bearer {os.environ["VERIFYET_SECRET_KEY"]}',
'Content-Type': 'application/json'
},
json={'token': token}
)
data = response.json()
if data.get('success'):
print(f"Payment verified! Amount: {data['verification']['amount']}")
return data['verification']
else:
raise Exception(data.get('error', 'Confirmation failed'))PHP Example
<?php
function confirmPayment($token) {
$ch = curl_init('https://api.verifyet.com/api/v1/verifications/confirm');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('VERIFYET_SECRET_KEY'),
'Content-Type: application/json'
],
CURLOPT_POSTFIELDS => json_encode(['token' => $token])
]);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
if ($data['success']) {
return $data['verification'];
}
throw new Exception($data['error'] ?? 'Confirmation failed');
}Webhooks
Receive real-time notifications when payments are verified.
Webhook Payload
{
"event": "verification.completed",
"data": {
"verification_id": "chk_abc123",
"status": "verified",
"transaction_id": "FT26028ABCDEF",
"amount": 1500,
"payer_name": "CUSTOMER NAME",
"receiver_name": "YOUR BUSINESS",
"service": "cbe",
"metadata": { "orderId": "123" },
"verified_at": 1706813800
},
"timestamp": 1706813801
}Verify Webhook Signature
const crypto = require('crypto');
function verifyWebhook(payload, signature, webhookSecret) {
const expected = crypto
.createHmac('sha256', webhookSecret)
.update(JSON.stringify(payload))
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// Express middleware
app.post('/webhook', (req, res) => {
const signature = req.headers['x-webhook-signature'];
if (!verifyWebhook(req.body, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const { event, data } = req.body;
if (event === 'verification.completed') {
// Process payment
processOrder(data.metadata.orderId, data);
}
res.status(200).send('OK');
});TypeScript Support
Full TypeScript support with exported types:
import {
VerifyEt,
VerifyEtConfig,
VerificationResult,
TransactionInfo,
VerifyEtError
} from 'verifyet-sdk';
const config: VerifyEtConfig = {
apiKey: 'pk_live_...',
debug: true,
locale: 'en',
theme: 'dark'
};
const verifyEt = new VerifyEt(config);
// With type-safe callbacks
verifyEt.verify({
mode: 'modal',
onSuccess: (result: VerificationResult) => {
console.log(result.token, result.amount);
},
onError: (error: VerifyEtError) => {
console.error(error.code, error.message);
}
});Browser Support
- Chrome 60+
- Firefox 55+
- Safari 11+
- Edge 79+
Security Best Practices
- Never expose your secret key in client-side code
- Always confirm tokens on your server before fulfilling orders
- Verify webhook signatures before processing
- Use HTTPS everywhere
- Implement idempotency to prevent double-processing
Troubleshooting
"Invalid API key" Error
- Check that you're using a publishable key (
pk_live_...) - Verify the key is correct and active
- Secret keys (
sk_live_...) should only be used server-side
Checkout Session Expired
- Default expiry is 15 minutes
- Increase
expiresInMinutesif needed - User must complete payment before timer runs out
CORS Errors
- SDK automatically handles CORS
- If self-hosting, ensure your API allows your domain
Local Development (localhost)
When developing locally, the SDK uses /api/v1 as the base URL (expecting a proxy). You need to configure your dev server to proxy API requests to https://api.verifyet.com.
Vite (vite.config.js):
export default {
server: {
proxy: {
'/api/v1': {
target: 'https://api.verifyet.com',
changeOrigin: true,
}
}
}
}Next.js (next.config.js):
module.exports = {
async rewrites() {
return [
{
source: '/api/v1/:path*',
destination: 'https://api.verifyet.com/api/v1/:path*',
},
];
},
};Alternative: Pass baseUrl explicitly to bypass auto-detection:
const verifyEt = new VerifyEt({
apiKey: 'pk_live_...',
baseUrl: 'https://api.verifyet.com/api/v1'
});Popup Blocked
checkout({ mode: 'popup' })may be blocked by browsers- SDK automatically falls back to redirect
- Recommended: Use
mode: 'modal'instead for a better UX that won't be blocked
Support
- Documentation: https://docs.verifyet.com
- Email: [email protected]
- GitHub: https://github.com/verifyet/sdk
