Order Payment Service
Order Payment Service provides one-time payment addresses for individual transactions. It's ideal for e-commerce checkouts, service payments, and any scenario where you need a unique payment address for each transaction.
What is Order Payment Service?
Order Payment Service allocates a temporary payment address for each order. When payment is received and confirmed, the order completes automatically and the address is released back to the pool for future use.
Key Characteristics:
- 🎯 One order, one address - Each order gets a unique payment address
- ⏱️ Time-limited - Orders have a payment window with grace period
- ♻️ Address recycling - Addresses return to pool after order completion
- 🔗 Single-chain - Each order monitors one specific blockchain
- 🏢 Multi-tenant - Complete organization isolation
Order vs Deposit
Understanding the difference between Order and Deposit services:
| Feature | Order Payment | Deposit Service |
|---|---|---|
| Address | Temporary (recycled after use) | Permanent (bound to user) |
| Lifetime | Minutes to hours | Long-term (months/years) |
| Monitoring | Single chain specified at creation | Multiple chains (protocol family) |
| Use Case | E-commerce checkout, invoices | User wallets, recurring top-ups |
| Expiration | Yes (payment window + grace period) | No expiration |
When to use Order Payment:
- E-commerce product checkout
- Service fee payments
- Invoice payments
- Event ticket purchases
- One-time donations
Quick Start
Prerequisites
Before creating orders, ensure you have:
- ✅ PayIn Account - Registered at testnet.payin.com or app.payin.com
- ✅ API Key - Generated from Admin dashboard
- ✅ Address Pool - At least a few addresses imported (see Address Pool Setup)
- ✅ Supported Network - Choose from supported networks
Check Address Pool First
If your address pool is empty, order creation will fail with "No available addresses in pool". Import addresses before proceeding.
Example 1: Create Your First Order (MCP)
Using PayIn with Claude Desktop or Cline:
Create a payment order:
- Order Reference: ORDER-2025-001
- Amount: 10 USDT
- Chain: ethereum-sepoliaThe AI assistant will:
- Call the
create_ordertool - Allocate a payment address from your pool
- Start monitoring the blockchain
- Return payment details with QR code
Expected Response:
✅ Order created successfully!
Order ID: 550e8400-e29b-41d4-a716-446655440123
Payment Address: 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb1
Amount: 10 USDT
Chain: ethereum-sepolia
Status: pending
Expires At: 2025-01-28T14:45:00Z
The buyer should send exactly 10 USDT to the address above.
Payment window: 10 minutes
Grace period: 5 minutes (total 15 minutes)Example 2: Create Order via API
Using cURL:
curl -X POST https://testnet.payin.com/api/v1/orders \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"orderReference": "ORDER-2025-001",
"amount": "10",
"currency": "USDT",
"chainId": "ethereum-sepolia"
}'Using TypeScript:
const response = await fetch('https://testnet.payin.com/api/v1/orders', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
orderReference: 'ORDER-2025-001',
amount: '10',
currency: 'USDT',
chainId: 'ethereum-sepolia'
})
});
const result = await response.json();
console.log('Order created:', result.data);Response:
{
"success": true,
"data": {
"orderId": "550e8400-e29b-41d4-a716-446655440123",
"paymentAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb1",
"amount": "10",
"currency": "USDT",
"chainId": "ethereum-sepolia",
"status": "pending",
"expiresAt": "2025-01-28T14:45:00Z",
"qrCode": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb1:10:USDT"
},
"message": "Order created successfully"
}Example 3: Check Order Status
Via API:
curl https://testnet.payin.com/api/v1/orders/550e8400-e29b-41d4-a716-446655440123 \
-H "Authorization: Bearer YOUR_API_KEY"Via MCP:
What's the status of order 550e8400-e29b-41d4-a716-446655440123?Response:
{
"success": true,
"data": {
"orderId": "550e8400-e29b-41d4-a716-446655440123",
"orderReference": "ORDER-2025-001",
"status": "completed",
"amount": "10",
"currency": "USDT",
"chainId": "ethereum-sepolia",
"paymentAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb1",
"confirmedReceived": "10.000000",
"completedAt": "2025-01-28T14:38:25Z",
"transactionHash": "0xabc123..."
}
}Order Lifecycle
Complete Flow Diagram
1. Create Order
↓
2. Allocate Address (from pool)
↓
3. Start Monitoring (blockchain + payment address)
↓
4. Payment Window (default: 10 minutes)
↓ [payment detected]
5. Wait for Confirmations (chain-specific: 3-10 blocks)
↓ [sufficient confirmations]
6. Order Completed
↓
7. Stop Monitoring
↓
8. Release Address (back to pool after cooldown)Status Transitions
PayIn uses a simple 3-state machine:
pending → completed (payment received and confirmed)
↓
→ expired (timeout without sufficient payment)Status Meanings:
pending: Order created, waiting for payment AND block confirmationscompleted: Payment received and confirmed successfullyexpired: Timeout reached without sufficient payment
Terminal States
Both completed and expired are terminal states - orders cannot transition out of these states.
Timeout Mechanism
Orders use a dual-timeout mechanism for reliability:
1. Payment Window (Default: 10 minutes)
- The primary time for user to complete payment
- After this window, no new payments should be initiated
- System continues monitoring during grace period
2. Grace Period (Default: 5 minutes)
- Extra time for pending transactions to confirm
- Ensures payments initiated before timeout can still complete
- After grace period, order becomes
expiredif insufficient payment
Total Timeout = Payment Window + Grace Period
// Example: Custom timeouts
{
"orderReference": "ORDER-2025-001",
"amount": "10",
"currency": "USDT",
"chainId": "ethereum-sepolia",
"paymentWindowMinutes": 15, // Custom: 15 minutes
"gracePeriodMinutes": 10 // Custom: 10 minutes
}Maximum Timeout
Total timeout cannot exceed system maximum (default: 60 minutes). Check with your PayIn configuration.
API Reference
Create Order
Create a new payment order with a unique payment address.
Endpoint: POST /api/v1/orders
Required Fields:
| Field | Type | Description |
|---|---|---|
orderReference | string | Your system's unique order identifier |
amount | string | Payment amount (e.g., "10", "99.50") |
currency | string | Token symbol (e.g., "USDT", "USDC") |
chainId | string | Blockchain identifier (e.g., "ethereum-sepolia") |
Optional Fields:
| Field | Type | Description |
|---|---|---|
paymentWindowMinutes | number | Custom payment window (default: 10) |
gracePeriodMinutes | number | Custom grace period (default: 5) |
successUrl | string | Redirect URL on order completion |
cancelUrl | string | Redirect URL on order expiration |
metadata | object | Custom data to attach to order |
Example Request:
{
"orderReference": "ORDER-2025-001",
"amount": "10",
"currency": "USDT",
"chainId": "ethereum-sepolia",
"successUrl": "https://myshop.com/orders/ORDER-2025-001/success",
"cancelUrl": "https://myshop.com/orders/ORDER-2025-001/cancel",
"metadata": {
"customer_id": "user_12345",
"product_id": "prod_xyz",
"source": "web_checkout"
}
}Response:
{
"success": true,
"data": {
"orderId": "550e8400-e29b-41d4-a716-446655440123",
"paymentAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb1",
"amount": "10",
"currency": "USDT",
"chainId": "ethereum-sepolia",
"status": "pending",
"expiresAt": "2025-01-28T14:45:00Z",
"qrCode": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb1:10:USDT"
}
}Get Order
Retrieve order details by order ID.
Endpoint: GET /api/v1/orders/:orderId
Response:
{
"success": true,
"data": {
"orderId": "550e8400-e29b-41d4-a716-446655440123",
"orderReference": "ORDER-2025-001",
"status": "completed",
"amount": "10",
"currency": "USDT",
"chainId": "ethereum-sepolia",
"paymentAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb1",
"confirmedReceived": "10.000000",
"requiredConfirmations": 3,
"paymentWindowEndsAt": "2025-01-28T14:40:00Z",
"gracePeriodEndsAt": "2025-01-28T14:45:00Z",
"createdAt": "2025-01-28T14:30:00Z",
"completedAt": "2025-01-28T14:38:25Z",
"metadata": {
"customer_id": "user_12345",
"product_id": "prod_xyz",
"source": "web_checkout"
}
}
}List Orders
List all orders with filtering and pagination.
Endpoint: GET /api/v1/orders
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status: pending, completed, expired |
chain | string | Filter by chain ID |
token | string | Filter by token symbol |
orderReference | string | Search by order reference |
createdAfter | ISO8601 | Filter by creation date (after) |
createdBefore | ISO8601 | Filter by creation date (before) |
page | number | Page number (default: 1) |
limit | number | Items per page (default: 20, max: 100) |
sortBy | string | Sort field: created_at, updated_at, amount |
sortOrder | string | Sort direction: ASC or DESC |
Example Request:
curl "https://testnet.payin.com/api/v1/orders?status=completed&chain=ethereum-sepolia&page=1&limit=10" \
-H "Authorization: Bearer YOUR_API_KEY"Response:
{
"success": true,
"data": {
"orders": [
{
"orderId": "550e8400-e29b-41d4-a716-446655440123",
"orderReference": "ORDER-2025-001",
"status": "completed",
"amount": "10",
"currency": "USDT",
"chainId": "ethereum-sepolia",
"completedAt": "2025-01-28T14:38:25Z"
}
],
"total": 42,
"page": 1,
"limit": 10
}
}Get Order Statistics
Get aggregated statistics for orders.
Endpoint: GET /api/v1/orders/stats
Query Parameters: Same as List Orders (for filtering)
Response:
{
"success": true,
"data": {
"totalOrders": 150,
"completedOrders": 120,
"pendingOrders": 5,
"expiredOrders": 25,
"totalAmount": "15234.50",
"completedAmount": "14120.00",
"avgPaymentTimeSeconds": 342,
"byStatus": {
"completed": 120,
"pending": 5,
"expired": 25
},
"byChain": {
"ethereum-sepolia": 80,
"polygon-amoy": 70
},
"byToken": {
"USDT": 100,
"USDC": 50
}
}
}Integration Examples
E-Commerce Checkout Flow
Complete integration example for an online store:
import { PayInClient } from '@payin/sdk'; // Hypothetical SDK
class CheckoutService {
private payin: PayInClient;
constructor(apiKey: string) {
this.payin = new PayInClient({
apiKey,
baseUrl: 'https://testnet.payin.com/api/v1',
});
}
/**
* Step 1: User clicks "Pay with Crypto" at checkout
*/
async createPaymentOrder(checkoutData: {
orderId: string;
totalAmount: number;
currency: string;
chain: string;
customerId: string;
items: any[];
}) {
try {
// Create PayIn order
const order = await this.payin.orders.create({
orderReference: checkoutData.orderId,
amount: checkoutData.totalAmount.toString(),
currency: checkoutData.currency,
chainId: checkoutData.chain,
successUrl: `https://myshop.com/checkout/success?order=${checkoutData.orderId}`,
cancelUrl: `https://myshop.com/checkout/cancel?order=${checkoutData.orderId}`,
metadata: {
customer_id: checkoutData.customerId,
items: checkoutData.items,
total_items: checkoutData.items.length,
},
});
// Store PayIn order ID in your database
await this.db.orders.update(checkoutData.orderId, {
payinOrderId: order.orderId,
paymentAddress: order.paymentAddress,
paymentStatus: 'awaiting_payment',
expiresAt: order.expiresAt,
});
return {
orderId: order.orderId,
paymentAddress: order.paymentAddress,
amount: order.amount,
currency: order.currency,
chain: order.chainId,
expiresAt: order.expiresAt,
qrCode: this.generateQRCode(order.paymentAddress, order.amount, order.currency),
};
} catch (error) {
console.error('Failed to create payment order:', error);
throw new Error('Payment creation failed. Please try again.');
}
}
/**
* Step 2: Display payment page to user
*/
renderPaymentPage(order: any) {
return {
paymentAddress: order.paymentAddress,
amount: order.amount,
currency: order.currency,
chain: order.chainId,
qrCode: order.qrCode,
expiresAt: order.expiresAt,
instructions: [
`Send exactly ${order.amount} ${order.currency} to the address above`,
`Network: ${order.chainId}`,
`Payment expires in ${this.getTimeRemaining(order.expiresAt)} minutes`,
],
};
}
/**
* Step 3: Poll for order status (alternative to webhooks)
*/
async pollOrderStatus(orderId: string): Promise<OrderStatus> {
const order = await this.payin.orders.get(orderId);
// Update your database
await this.db.orders.updateByPayinOrderId(orderId, {
paymentStatus: order.status,
confirmedAmount: order.confirmedReceived,
completedAt: order.completedAt,
});
return order.status;
}
/**
* Step 4: Handle webhook notification (recommended)
*/
async handleWebhook(event: PayInWebhookEvent) {
// Verify webhook signature (important for security!)
if (!this.payin.webhooks.verify(event)) {
throw new Error('Invalid webhook signature');
}
if (event.type === 'order.completed') {
const orderReference = event.data.orderReference;
// Update your order status
await this.db.orders.update(orderReference, {
paymentStatus: 'paid',
paidAt: event.data.completedAt,
transactionHash: event.data.transactionHash,
});
// Trigger fulfillment
await this.fulfillmentService.processOrder(orderReference);
// Send confirmation email
await this.emailService.sendPaymentConfirmation(orderReference);
} else if (event.type === 'order.expired') {
const orderReference = event.data.orderReference;
// Mark as expired
await this.db.orders.update(orderReference, {
paymentStatus: 'expired',
});
// Send expiration notice
await this.emailService.sendPaymentExpired(orderReference);
}
}
private generateQRCode(address: string, amount: string, currency: string): string {
// Generate QR code with payment details
// Implementation depends on your QR library
return `data:image/png;base64,...`;
}
private getTimeRemaining(expiresAt: Date): number {
return Math.floor((new Date(expiresAt).getTime() - Date.now()) / 60000);
}
}Service Payment (Consulting Invoice)
Example for service-based businesses:
class InvoicePaymentService {
async createInvoicePayment(invoice: {
invoiceNumber: string;
clientId: string;
amount: number;
description: string;
dueDate: Date;
}) {
// Create PayIn order with longer payment window for invoices
const order = await fetch('https://testnet.payin.com/api/v1/orders', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.PAYIN_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
orderReference: invoice.invoiceNumber,
amount: invoice.amount.toString(),
currency: 'USDT',
chainId: 'ethereum-sepolia',
paymentWindowMinutes: 4320, // 3 days (72 hours)
gracePeriodMinutes: 1440, // 1 day (24 hours)
successUrl: `https://consulting.com/invoices/${invoice.invoiceNumber}/paid`,
metadata: {
client_id: invoice.clientId,
description: invoice.description,
due_date: invoice.dueDate.toISOString(),
invoice_type: 'consulting_services',
},
}),
});
const result = await order.json();
if (!result.success) {
throw new Error(`Failed to create payment order: ${result.message}`);
}
// Send invoice email with payment instructions
await this.sendInvoiceEmail(invoice, result.data);
return result.data;
}
private async sendInvoiceEmail(invoice: any, paymentOrder: any) {
const emailContent = `
Invoice: ${invoice.invoiceNumber}
Amount Due: ${paymentOrder.amount} ${paymentOrder.currency}
Payment Instructions:
1. Send ${paymentOrder.amount} ${paymentOrder.currency} to:
${paymentOrder.paymentAddress}
2. Network: ${paymentOrder.chainId}
3. Payment expires: ${new Date(paymentOrder.expiresAt).toLocaleString()}
QR Code attached for easy payment.
`;
// Send email with payment details
// ...
}
}Advanced Configuration
Custom Payment Window
Adjust timeouts based on your use case:
// Quick payment (event tickets)
{
"paymentWindowMinutes": 5, // 5 minutes to pay
"gracePeriodMinutes": 3 // 3 minutes grace
}
// Standard e-commerce
{
"paymentWindowMinutes": 10, // Default
"gracePeriodMinutes": 5 // Default
}
// Invoice payment
{
"paymentWindowMinutes": 4320, // 3 days
"gracePeriodMinutes": 1440 // 1 day
}Choosing Payment Window
- Short (5-10 min): Time-sensitive items (event tickets, limited stock)
- Medium (15-30 min): Standard e-commerce checkout
- Long (hours/days): Invoices, B2B payments, high-value items
Redirect URLs
Configure where users go after payment:
{
"orderReference": "ORDER-2025-001",
"amount": "10",
"currency": "USDT",
"chainId": "ethereum-sepolia",
// Redirect URLs with order information
"successUrl": "https://myshop.com/orders/ORDER-2025-001/success",
"cancelUrl": "https://myshop.com/orders/ORDER-2025-001/cancel"
}URL Parameters Added by PayIn:
When redirecting, PayIn appends these parameters:
?order_reference=ORDER-2025-001
&status=completed
&amount=10
¤cy=USDT
&chain_id=ethereum-sepolia
&completed_at=2025-01-28T14:38:25Z
&tx_hash=0xabc123...Full Redirect Example:
https://myshop.com/orders/ORDER-2025-001/success?order_reference=ORDER-2025-001&status=completed&amount=10¤cy=USDT&chain_id=ethereum-sepolia&completed_at=2025-01-28T14:38:25Z&tx_hash=0xabc123...Default Redirect URLs
You can configure organization-wide default redirect URLs in PayIn Admin dashboard. Order-level URLs take precedence over defaults.
Order Metadata
Attach custom data to orders:
{
"orderReference": "ORDER-2025-001",
"amount": "10",
"currency": "USDT",
"chainId": "ethereum-sepolia",
"metadata": {
// Customer information
"customer_id": "user_12345",
"customer_email": "buyer@example.com",
// Order details
"product_ids": ["prod_abc", "prod_xyz"],
"shipping_address_id": "addr_789",
"discount_code": "SAVE10",
// Internal tracking
"source": "web_checkout",
"campaign": "summer_sale_2025",
"referrer": "google_ads",
// Any custom fields
"notes": "Gift wrapping requested"
}
}Metadata is:
- ✅ Stored with the order
- ✅ Returned in API responses
- ✅ Included in webhook events
- ✅ Fully searchable via API
Best Practices
Order Reference Design
Use structured, unique order references:
Good Examples:
ORDER-2025-001
INV-2025-Q1-12345
TICKET-EVENT789-SEAT42Avoid:
order1 // Not unique enough
12345 // No context
tmp_order // Not permanentUniqueness is Critical
Order references must be unique within your organization. PayIn will reject duplicate order references to prevent accidental double-charging.
Amount Precision
Always use string format for amounts to avoid floating-point errors:
Correct:
{
"amount": "10.50" // ✅ String
"amount": "99.99" // ✅ String
"amount": "1000.000" // ✅ String with extra precision
}Incorrect:
{
"amount": 10.50 // ❌ Number (may lose precision)
"amount": 0.1 + 0.2 // ❌ JavaScript floating-point error (0.30000000000000004)
}Error Handling
Handle all error scenarios:
try {
const order = await payin.orders.create(orderData);
return order;
} catch (error) {
if (error.code === 'INSUFFICIENT_ADDRESSES') {
// No available addresses in pool
return {
error: 'Payment system temporarily unavailable. Please try again in a few minutes.',
shouldRetry: true,
};
} else if (error.code === 'DUPLICATE_ORDER_REFERENCE') {
// Order reference already exists
return {
error: 'This order has already been created.',
shouldRetry: false,
};
} else if (error.code === 'INVALID_CHAIN_TOKEN_COMBINATION') {
// Token not available on selected chain
return {
error: 'Selected payment method not available. Please choose another option.',
shouldRetry: false,
};
} else {
// Unknown error
console.error('Order creation failed:', error);
return {
error: 'Failed to create payment order. Please contact support.',
shouldRetry: false,
};
}
}Monitoring Order Status
Option 1: Webhooks (Recommended)
// Configure webhook endpoint in Admin dashboard
// https://myshop.com/webhooks/payin
app.post('/webhooks/payin', async (req, res) => {
const event = req.body;
// Verify signature (critical!)
const signature = req.headers['x-payin-signature'];
if (!verifySignature(event, signature)) {
return res.status(401).send('Invalid signature');
}
// Process event
if (event.type === 'order.completed') {
await handleOrderCompleted(event.data);
} else if (event.type === 'order.expired') {
await handleOrderExpired(event.data);
}
res.status(200).send('OK');
});Option 2: Polling (Fallback)
// Poll order status every 10 seconds
async function pollOrderStatus(orderId: string) {
const maxAttempts = 90; // 15 minutes (90 * 10 seconds)
let attempts = 0;
const interval = setInterval(async () => {
attempts++;
try {
const order = await payin.orders.get(orderId);
if (order.status === 'completed') {
clearInterval(interval);
await handleOrderCompleted(order);
} else if (order.status === 'expired') {
clearInterval(interval);
await handleOrderExpired(order);
} else if (attempts >= maxAttempts) {
clearInterval(interval);
console.warn('Max polling attempts reached for order:', orderId);
}
} catch (error) {
console.error('Failed to fetch order status:', error);
}
}, 10000); // 10 seconds
}Webhooks vs Polling
Webhooks are the recommended approach for production systems. Polling should only be used as a fallback or during development.
Testing Strategy
1. Testnet Testing:
// Use testnet for all development and testing
const payin = new PayInClient({
apiKey: process.env.PAYIN_TESTNET_API_KEY,
baseUrl: 'https://testnet.payin.com/api/v1',
});
// Get testnet tokens from faucets
// Ethereum Sepolia: https://sepolia-faucet.pk910.de/
// Polygon Amoy: https://faucet.polygon.technology/2. Test Scenarios:
- ✅ Successful payment (exact amount)
- ✅ Payment timeout (no payment sent)
- ✅ Partial payment (insufficient amount)
- ✅ Overpayment (more than required)
- ✅ Multiple payments (cumulative amount)
- ✅ Network failures and retries
- ✅ Webhook delivery and signature verification
3. Mainnet Migration:
// Only switch to mainnet after thorough testnet testing
const payin = new PayInClient({
apiKey: process.env.PAYIN_MAINNET_API_KEY,
baseUrl: 'https://app.payin.com/api/v1', // Production URL
});Troubleshooting
"No available addresses in pool"
Problem: Order creation fails with address pool error.
Solution:
- Check address pool status in Admin dashboard
- Import more addresses following Address Pool Setup
- Ensure addresses for the correct protocol (EVM/Tron)
# Check pool status via API
curl https://testnet.payin.com/api/v1/address-pool/status \
-H "Authorization: Bearer YOUR_API_KEY"Order Expired but Payment Sent
Problem: User sent payment but order expired before confirmation.
Causes:
- Network congestion (slow block confirmations)
- User sent payment very close to expiration
- Wrong network gas settings (too low gas price)
Solutions:
- Increase grace period for high-value orders
- Contact support with transaction hash - PayIn can manually reconcile
- Refund customer if payment arrived too late (you have the funds)
Wrong Chain or Token
Problem: User sent wrong token or on wrong network.
Prevention:
- Clearly display network and token on payment page
- Show network name (not just chain ID)
- Include warnings about correct network
If it happens:
- Funds sent to wrong address cannot be recovered automatically
- User must contact your support with transaction details
- PayIn support can assist with address verification
Duplicate Order Reference
Problem: API returns error about duplicate order reference.
Cause: Attempting to create order with already-used reference.
Solution:
// Generate unique references
const orderReference = `ORDER-${Date.now()}-${generateRandomId()}`;
// Or check for existing orders first
const existing = await payin.orders.list({
orderReference: proposedReference,
});
if (existing.total > 0) {
throw new Error('Order reference already exists');
}Next Steps
Essential Guides
- Webhooks - Set up automated event notifications
- API Integration - Complete API documentation
- Security - Secure your integration
Related Services
- Deposit Service - Permanent user addresses for recurring payments
- Payment Links - No-code payment collection
Technical References
- Supported Networks - Available blockchains
- Supported Tokens - Available stablecoins
- Address Management - Advanced address pool management
Support
Need help with Order Payment Service?
- 📧 Email: support@payin.com
- 💬 Discord: Join our community
- 🤖 AI Assistant: Ask your MCP-enabled AI assistant