Basic Payment Example
Complete example of processing a payment using Payme Merchant API.
Overview
This example demonstrates:
- Checking if payment is possible
- Creating a transaction
- Performing the transaction
- Handling errors
- Database integration
Prerequisites
bash
bun add @joyida/paymeEnvironment Setup
Create .env file:
bash
PAYME_MERCHANT_ID=your_merchant_id
PAYME_SECRET_KEY=your_secret_key
DATABASE_URL=./app.dbDatabase Schema
sql
-- Create transactions table
CREATE TABLE IF NOT EXISTS payme_transactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
payme_id TEXT UNIQUE NOT NULL,
order_id TEXT NOT NULL,
amount INTEGER NOT NULL,
state INTEGER NOT NULL DEFAULT 1,
reason INTEGER,
create_time INTEGER NOT NULL,
perform_time INTEGER DEFAULT 0,
cancel_time INTEGER DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- Create orders table
CREATE TABLE IF NOT EXISTS orders (
id TEXT PRIMARY KEY,
user_id INTEGER NOT NULL,
amount INTEGER NOT NULL,
status TEXT DEFAULT 'pending',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- Create indexes
CREATE INDEX idx_payme_transactions_payme_id ON payme_transactions(payme_id);
CREATE INDEX idx_payme_transactions_order_id ON payme_transactions(order_id);
CREATE INDEX idx_orders_user_id ON orders(user_id);Complete Implementation
typescript
import { PaymeMerchant, TransactionStates, PaymeError, ValidationError, NetworkError } from '@joyida/payme';
import { Database } from 'bun:sqlite';
// Initialize Payme client
const payme = new PaymeMerchant({
merchantId: process.env.PAYME_MERCHANT_ID!,
secretKey: process.env.PAYME_SECRET_KEY!,
timeout: 30000
});
// Initialize database
const db = new Database(process.env.DATABASE_URL!);
// Generate 24-character Payme transaction ID
function generatePaymeId(): string {
const chars = '0123456789abcdef';
let id = '';
for (let i = 0; i < 24; i++) {
id += chars[Math.floor(Math.random() * chars.length)];
}
return id;
}
// Check if order exists and get details
function getOrder(orderId: string) {
const order = db.query(`
SELECT * FROM orders WHERE id = ? AND status = 'pending'
`).get(orderId);
if (!order) {
throw new Error('Order not found or already processed');
}
return order;
}
// Save transaction to database
function saveTransaction(data: {
paymeId: string;
orderId: string;
amount: number;
state: number;
createTime: number;
}) {
db.run(`
INSERT INTO payme_transactions
(payme_id, order_id, amount, state, create_time)
VALUES (?, ?, ?, ?, ?)
`, [
data.paymeId,
data.orderId,
data.amount,
data.state,
data.createTime
]);
}
// Update transaction state
function updateTransactionState(paymeId: string, state: number, performTime?: number) {
if (performTime) {
db.run(`
UPDATE payme_transactions
SET state = ?, perform_time = ?, updated_at = CURRENT_TIMESTAMP
WHERE payme_id = ?
`, [state, performTime, paymeId]);
} else {
db.run(`
UPDATE payme_transactions
SET state = ?, updated_at = CURRENT_TIMESTAMP
WHERE payme_id = ?
`, [state, paymeId]);
}
}
// Update order status
function updateOrderStatus(orderId: string, status: string) {
db.run(`
UPDATE orders
SET status = ?, updated_at = CURRENT_TIMESTAMP
WHERE id = ?
`, [status, orderId]);
}
// Main payment processing function
async function processPayment(orderId: string) {
console.log(`\n🔄 Processing payment for order: ${orderId}`);
try {
// 1. Get order details
console.log('📋 Fetching order details...');
const order = getOrder(orderId);
console.log(`✅ Order found: ${order.amount} tiyin`);
// 2. Check if payment is possible
console.log('\n🔍 Checking if payment is possible...');
const check = await payme.checkPerformTransaction({
amount: order.amount,
account: { order_id: orderId }
});
if (!check.allow) {
throw new Error('Payment not allowed');
}
console.log('✅ Payment allowed');
// 3. Create transaction
console.log('\n📝 Creating transaction...');
const paymeId = generatePaymeId();
console.log(`Generated Payme ID: ${paymeId}`);
const created = await payme.createTransaction({
id: paymeId,
time: Date.now(),
amount: order.amount,
account: { order_id: orderId }
});
console.log(`✅ Transaction created: ${created.transaction}`);
console.log(` State: ${created.state} (created)`);
console.log(` Create time: ${new Date(created.create_time).toISOString()}`);
// 4. Save to database
console.log('\n💾 Saving to database...');
saveTransaction({
paymeId,
orderId,
amount: order.amount,
state: created.state,
createTime: created.create_time
});
updateOrderStatus(orderId, 'awaiting_payment');
console.log('✅ Saved to database');
// 5. Perform transaction (complete payment)
console.log('\n💳 Performing transaction...');
const performed = await payme.performTransaction({
id: paymeId
});
console.log(`✅ Transaction performed: ${performed.transaction}`);
console.log(` State: ${performed.state} (completed)`);
console.log(` Perform time: ${new Date(performed.perform_time).toISOString()}`);
// 6. Update database
console.log('\n💾 Updating database...');
updateTransactionState(paymeId, performed.state, performed.perform_time);
updateOrderStatus(orderId, 'paid');
console.log('✅ Database updated');
// 7. Check final status
console.log('\n🔍 Checking final status...');
const status = await payme.checkTransaction({
id: paymeId
});
console.log('✅ Final status:');
console.log(` State: ${status.state}`);
console.log(` Created: ${new Date(status.create_time).toISOString()}`);
console.log(` Performed: ${new Date(status.perform_time).toISOString()}`);
if (status.state === TransactionStates.COMPLETED) {
console.log('\n🎉 Payment successful!');
return {
success: true,
paymeId,
transactionId: status.transaction,
amount: order.amount
};
} else {
throw new Error(`Unexpected transaction state: ${status.state}`);
}
} catch (error) {
console.error('\n❌ Payment failed!');
if (error instanceof ValidationError) {
console.error('Validation error:', error.message);
throw new Error(`Invalid input: ${error.message}`);
} else if (error instanceof PaymeError) {
console.error('Payme error:', error.code, error.message);
switch (error.code) {
case -32504:
throw new Error('Payment system configuration error');
case -31001:
throw new Error('Invalid payment amount');
case -31003:
throw new Error('Transaction not found');
case -31050:
throw new Error('Order not found');
case -32400:
throw new Error('Payment system temporarily unavailable');
default:
throw new Error(`Payment error: ${error.message}`);
}
} else if (error instanceof NetworkError) {
console.error('Network error:', error.message);
if (error.timeout) {
throw new Error(`Request timed out after ${error.timeout}ms`);
} else {
throw new Error('Network connection error');
}
} else {
console.error('Unknown error:', error);
throw error;
}
}
}
// Example usage
async function main() {
// Create a test order
const orderId = `ORD-${Date.now()}`;
const amount = 500000; // 5000 UZS in tiyin
db.run(`
INSERT INTO orders (id, user_id, amount, status)
VALUES (?, ?, ?, ?)
`, [orderId, 1, amount, 'pending']);
console.log(`Created test order: ${orderId}`);
// Process payment
try {
const result = await processPayment(orderId);
console.log('\n✅ Payment result:', result);
} catch (error) {
console.error('\n❌ Payment error:', error.message);
process.exit(1);
}
}
// Run example
main();Running the Example
bash
# Install dependencies
bun install
# Set environment variables
export PAYME_MERCHANT_ID=your_merchant_id
export PAYME_SECRET_KEY=your_secret_key
# Run the example
bun run example.tsExpected Output
Created test order: ORD-1234567890
🔄 Processing payment for order: ORD-1234567890
📋 Fetching order details...
✅ Order found: 500000 tiyin
🔍 Checking if payment is possible...
✅ Payment allowed
📝 Creating transaction...
Generated Payme ID: 5305e3bab097f420a62ced0b
✅ Transaction created: 5123
State: 1 (created)
Create time: 2024-01-15T10:30:00.000Z
💾 Saving to database...
✅ Saved to database
💳 Performing transaction...
✅ Transaction performed: 5123
State: 2 (completed)
Perform time: 2024-01-15T10:30:01.000Z
💾 Updating database...
✅ Database updated
🔍 Checking final status...
✅ Final status:
State: 2
Created: 2024-01-15T10:30:00.000Z
Performed: 2024-01-15T10:30:01.000Z
🎉 Payment successful!
✅ Payment result: {
success: true,
paymeId: '5305e3bab097f420a62ced0b',
transactionId: '5123',
amount: 500000
}Error Handling Examples
Invalid Amount
typescript
// This will throw ValidationError
await payme.createTransaction({
id: paymeId,
time: Date.now(),
amount: -100, // ❌ Negative amount
account: { order_id: orderId }
});
// Output:
// ❌ Payment failed!
// Validation error: Amount must be a positive integerOrder Not Found
typescript
// This will throw PaymeError with code -31050
await payme.checkPerformTransaction({
amount: 500000,
account: { order_id: 'NON_EXISTENT' } // ❌ Order doesn't exist
});
// Output:
// ❌ Payment failed!
// Payme error: -31050 Order not foundNetwork Timeout
typescript
// This will throw NetworkError
const payme = new PaymeMerchant({
merchantId: process.env.PAYME_MERCHANT_ID!,
secretKey: process.env.PAYME_SECRET_KEY!,
timeout: 1 // ❌ Very short timeout
});
// Output:
// ❌ Payment failed!
// Network error: Request timed out after 1msNext Steps
- Learn about Recurring Payments
- Read the Payment Flow and Database Integration guides
- See Error Handling