Tóm tắt
API iPay cho phép nhà phát triển tích hợp xử lý thanh toán, lập hóa đơn, và giao dịch tài chính theo chương trình. Nó sử dụng xác thực OAuth 2.0, khóa API, cung cấp các endpoint RESTful cho thanh toán, hoàn tiền, giao dịch và đối chiếu. Để triển khai thực tế, bạn cần tuân thủ PCI DSS và các giới hạn tỷ lệ chuẩn ngành. Bài viết này hướng dẫn từng bước: thiết lập xác thực, xử lý thanh toán, tích hợp webhook, tuân thủ bảo mật và checklist triển khai production.
Hãy dùng thử Apidog ngay hôm nay
💡 Apidog đơn giản hóa kiểm thử API thanh toán: Kiểm thử endpoint thanh toán ở chế độ sandbox, xác thực chữ ký webhook, kiểm tra phản hồi giao dịch và debug các vấn đề tích hợp trong một workspace duy nhất. Nhập spec API, giả lập response và chia sẻ test case cho cả team.
Lưu ý: Bài viết sử dụng mẫu tích hợp API thanh toán phổ biến, áp dụng với iPay và các dịch vụ tương tự. Luôn kiểm tra lại tài liệu chính thức của iPay cho từng endpoint và xác thực.
API iPay là gì?
API thanh toán như iPay cung cấp RESTful endpoint để xử lý giao dịch tài chính, bao gồm:
- Ủy quyền & thu tiền thanh toán
- Hoàn tiền & bồi hoàn (chargebacks)
- Lịch sử giao dịch, báo cáo
- Mã hóa thông tin khách hàng (vault)
- Đăng ký & thanh toán định kỳ
- Tạo/quản lý hóa đơn
- Đối chiếu, thanh toán bù trừ
- Ngăn chặn gian lận
Các tính năng chính
| Tính năng | Mô tả |
|---|---|
| API RESTful | Endpoint dựa trên JSON |
| OAuth 2.0 + Khóa API | Xác thực an toàn |
| Webhooks | Thông báo thanh toán real-time |
| Mã hóa token | Lưu trữ thẻ an toàn |
| 3D Secure | Tuân thủ SCA |
| PCI DSS | Yêu cầu tuân thủ Cấp độ 1 |
| Đa tiền tệ | Hỗ trợ >100 loại tiền tệ |
| Chống gian lận | Chấm điểm rủi ro, kiểm tra tốc độ |
Tổng quan về luồng thanh toán
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Khách hàng │───▶│ Người bán │───▶│ Cổng TT │
│ (Browser) │ │ (Server) │ │ Thanh toán │
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
│ Nhập thẻ │ │
│───────────────▶│ │
│ │ │
│ Mã hóa token │ │
│───────────────▶│ Tạo ý định │
│ │───────────────▶ │
│ │ │
│ │ Xác nhận TT │
│ │───────────────▶ │
│ │ │
│ │ Kết quả │
│ │◀─────────────── │
│ │ │
│ Biên nhận │ │
│◀───────────────│ │
Môi trường API
| Môi trường | URL | Trường hợp sử dụng |
|---|---|---|
| Sandbox | https://sandbox.ipay.com/api |
Phát triển, kiểm thử |
| Production | https://api.ipay.com/api |
Giao dịch thực tế |
Bắt đầu: Thiết lập xác thực
Bước 1: Tạo tài khoản iPay
- Đăng ký tài khoản người bán iPay.
- Hoàn tất xác minh doanh nghiệp (KYB).
- Gửi giấy tờ: đăng ký kinh doanh, tài khoản ngân hàng, giấy tờ tùy thân.
- Chờ phê duyệt (1-3 ngày làm việc).
Bước 2: Lấy thông tin xác thực API
- Đăng nhập dashboard người bán iPay.
- Vào Cài đặt > Khóa API.
- Tạo khóa API mới.
- Lưu khóa & secret ở nơi an toàn.
# .env (KHÔNG commit lên git)
IPAY_API_KEY="live_xxxxxx"
IPAY_API_SECRET="secret_xxxxxx"
IPAY_WEBHOOK_SECRET="whsec_xxxxxx"
Lưu ý: Dùng khóa riêng cho sandbox & production.
Bước 3: Hiểu các phương pháp xác thực
| Phương pháp | Tốt nhất cho | Mức bảo mật |
|---|---|---|
| Basic Auth | Server-to-server | Cao |
| OAuth 2.0 | Ứng dụng đa tenant | Cao hơn |
| JWT | Microservices | Cao |
Bước 4: Gọi API xác thực
const IPAY_BASE_URL = process.env.IPAY_SANDBOX
? 'https://sandbox.ipay.com/api'
: 'https://api.ipay.com/api';
const ipayRequest = async (endpoint, options = {}) => {
const apiKey = process.env.IPAY_API_KEY;
const apiSecret = process.env.IPAY_API_SECRET;
// Basic Auth (Base64)
const authHeader = Buffer.from(`${apiKey}:${apiSecret}`).toString('base64');
const response = await fetch(`${IPAY_BASE_URL}${endpoint}`, {
...options,
headers: {
'Authorization': `Basic ${authHeader}`,
'Content-Type': 'application/json',
'Idempotency-Key': options.idempotencyKey || generateIdempotencyKey(),
...options.headers
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Lỗi API iPay: ${error.message}`);
}
return response.json();
};
function generateIdempotencyKey() {
return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
// Sử dụng
const account = await ipayRequest('/account');
console.log(`Người bán: ${account.business_name}`);
Xử lý thanh toán
Tạo ý định thanh toán
const createPayment = async (paymentData) => {
const payment = {
amount: paymentData.amount, // Đơn vị nhỏ nhất (cent)
currency: paymentData.currency || 'USD',
customer: paymentData.customerId,
payment_method: paymentData.paymentMethodId,
confirm: true,
description: paymentData.description,
metadata: {
orderId: paymentData.orderId,
customerId: paymentData.customerId
},
capture_method: paymentData.captureMethod || 'automatic',
statement_descriptor: paymentData.statementDescriptor || 'MYCOMPANY'
};
const response = await ipayRequest('/payments', {
method: 'POST',
body: JSON.stringify(payment),
idempotencyKey: paymentData.idempotencyKey
});
return response;
};
// Sử dụng
const payment = await createPayment({
amount: 2999, // 29,99 USD
currency: 'USD',
customerId: 'cus_12345',
paymentMethodId: 'pm_67890',
description: 'Đơn hàng #ORD-001',
orderId: 'ORD-001',
statementDescriptor: 'MYCOMPANY INC'
});
console.log(`Trạng thái thanh toán: ${payment.status}`);
console.log(`ID thanh toán: ${payment.id}`);
Luồng trạng thái thanh toán
yêu_cầu_phương_thức_thanh_toán → yêu_cầu_xác_nhận → yêu_cầu_hành_động
→ đang_xử_lý → yêu_cầu_thu_tiền → thành_công
→ thất_bại
→ đã_hủy
Phương thức thanh toán
| Phương thức | Loại | Trường hợp sử dụng |
|---|---|---|
card |
Thẻ tín dụng/ghi nợ | Thanh toán chuẩn |
bank_transfer |
ACH, SEPA | Chuyển khoản phí thấp |
digital_wallet |
Apple Pay, Google Pay | Thanh toán di động |
buy_now_pay_later |
Klarna, Afterpay | Thanh toán trả góp |
Mã hóa Token thông tin thẻ
const tokenizeCard = async (cardData) => {
// KHÔNG gửi thẻ thô lên server - mã hóa phía client
const response = await fetch(`${IPAY_BASE_URL}/tokens`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${CLIENT_PUBLISHABLE_KEY}`
},
body: JSON.stringify({
card: {
number: cardData.number,
exp_month: cardData.expMonth,
exp_year: cardData.expYear,
cvc: cardData.cvc
}
})
});
const token = await response.json();
return token; // Gửi token.id về server
};
// Server: tạo payment method từ token
const createPaymentMethod = async (tokenId, customerId) => {
const response = await ipayRequest('/payment_methods', {
method: 'POST',
body: JSON.stringify({
type: 'card',
token: tokenId,
customer: customerId
})
});
return response;
};
Xác thực 3D Secure
const createPaymentWith3DS = async (paymentData) => {
const payment = await createPayment({
...paymentData,
confirmation_token: true // Trả về client secret cho 3DS
});
if (payment.status === 'requires_action') {
// Client xử lý thử thách 3DS
return {
requiresAction: true,
clientSecret: payment.client_secret,
nextAction: payment.next_action
};
}
return { success: true, payment };
};
// Client: dùng iPay.js hoặc SDK để hiển thị thử thách 3DS
Quản lý hoàn tiền
Hoàn tiền toàn bộ
const refundPayment = async (paymentId, reason = null) => {
const refund = {
payment: paymentId,
reason: reason || 'khách hàng yêu cầu'
};
const response = await ipayRequest('/refunds', {
method: 'POST',
body: JSON.stringify(refund),
idempotencyKey: `refund_${paymentId}_${Date.now()}`
});
return response;
};
// Sử dụng
const refund = await refundPayment('pay_12345', 'trùng lặp');
console.log(`Trạng thái hoàn tiền: ${refund.status}`);
console.log(`ID hoàn tiền: ${refund.id}`);
Hoàn tiền một phần
const partialRefund = async (paymentId, amount, reason = null) => {
const refund = {
payment: paymentId,
amount: amount, // Đơn vị nhỏ nhất
reason: reason || 'khách hàng yêu cầu'
};
const response = await ipayRequest('/refunds', {
method: 'POST',
body: JSON.stringify(refund),
idempotencyKey: `refund_${paymentId}_${amount}_${Date.now()}`
});
return response;
};
// Sử dụng - Hoàn 15,00 USD từ khoản 29,99 USD
const refund = await partialRefund('pay_12345', 1500, 'giao hàng một phần');
console.log(`Đã hoàn tiền: $${refund.amount / 100}`);
Lý do hoàn tiền
| Mã lý do | Mô tả |
|---|---|
duplicate |
Giao dịch trùng lặp |
fraudulent |
Giao dịch gian lận |
requested_by_customer |
Yêu cầu khách hàng |
order_canceled |
Hủy đơn hàng |
product_not_received |
Chưa nhận sản phẩm |
product_not_as_described |
Không như mô tả |
Quản lý khách hàng
Tạo khách hàng
const createCustomer = async (customerData) => {
const customer = {
email: customerData.email,
name: customerData.name,
phone: customerData.phone,
metadata: {
internalId: customerData.internalId,
tier: customerData.tier
}
};
const response = await ipayRequest('/customers', {
method: 'POST',
body: JSON.stringify(customer)
});
return response;
};
// Sử dụng
const customer = await createCustomer({
email: 'customer@example.com',
name: 'John Doe',
phone: '+1-555-0123',
internalId: 'USR-12345',
tier: 'premium'
});
console.log(`Đã tạo khách hàng: ${customer.id}`);
Gắn phương thức thanh toán
const attachPaymentMethod = async (paymentMethodId, customerId) => {
const response = await ipayRequest(`/payment_methods/${paymentMethodId}/attach`, {
method: 'POST',
body: JSON.stringify({
customer: customerId
})
});
return response;
};
// Sử dụng
await attachPaymentMethod('pm_67890', 'cus_12345');
Liệt kê phương thức thanh toán của khách
const getCustomerPaymentMethods = async (customerId) => {
const response = await ipayRequest(`/customers/${customerId}/payment_methods`);
return response;
};
// Sử dụng
const methods = await getCustomerPaymentMethods('cus_12345');
methods.data.forEach(method => {
console.log(`${method.card.brand} kết thúc bằng ${method.card.last4}`);
console.log(`Hết hạn: ${method.card.exp_month}/${method.card.exp_year}`);
});
Webhooks
Cấu hình Webhooks
- Đăng nhập dashboard iPay.
- Vào Nhà phát triển > Webhooks.
- Thêm endpoint HTTPS của bạn.
- Chọn sự kiện cần đăng ký.
Sự kiện Webhook
| Sự kiện | Kích hoạt |
|---|---|
payment.succeeded |
Thanh toán hoàn tất |
payment.failed |
Thanh toán bị từ chối |
payment.refunded |
Hoàn tiền thành công |
payment.disputed |
Đã có tranh chấp |
customer.created |
Khách hàng mới |
customer.subscription.updated |
Đăng ký thay đổi |
Xử lý Webhooks
const express = require('express');
const crypto = require('crypto');
const app = express();
app.post('/webhooks/ipay', express.raw({ type: 'application/json' }), async (req, res) => {
const signature = req.headers['ipay-signature'];
const payload = req.body;
// Xác minh chữ ký webhook
const isValid = verifyWebhookSignature(payload, signature, process.env.IPAY_WEBHOOK_SECRET);
if (!isValid) {
console.error('Chữ ký webhook không hợp lệ');
return res.status(401).send('Không được ủy quyền');
}
const event = JSON.parse(payload.toString());
// Định tuyến sự kiện
switch (event.type) {
case 'payment.succeeded':
await handlePaymentSucceeded(event.data);
break;
case 'payment.failed':
await handlePaymentFailed(event.data);
break;
case 'payment.refunded':
await handlePaymentRefunded(event.data);
break;
case 'payment.disputed':
await handlePaymentDisputed(event.data);
break;
default:
console.log('Sự kiện chưa xử lý:', event.type);
}
res.status(200).send('OK');
});
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expectedSignature, 'hex')
);
}
async function handlePaymentSucceeded(data) {
console.log(`Thanh toán thành công: ${data.id}`);
// Cập nhật đơn hàng
await db.orders.update(data.metadata.orderId, {
status: 'paid',
paymentId: data.id,
paidAt: new Date()
});
// Gửi email xác nhận
await sendOrderConfirmation(data.metadata.orderId);
}
async function handlePaymentFailed(data) {
console.log(`Thanh toán thất bại: ${data.id} - ${data.failure_code}`);
// Thông báo cho khách
await sendPaymentFailedEmail(data.customer, data.failure_message);
// Đánh dấu đơn hàng lỗi
await db.orders.update(data.metadata.orderId, {
status: 'payment_failed',
failureReason: data.failure_message
});
}
Bảo mật và Tuân thủ
Yêu cầu PCI DSS
| Yêu cầu | Triển khai |
|---|---|
| Mạng an toàn | Sử dụng HTTPS, firewall, cấu hình an toàn |
| Bảo vệ chủ thẻ | Không lưu CVV, mã hóa PAN |
| Quản lý lỗ hổng | Update bảo mật, antivirus |
| Kiểm soát truy cập | Quyền tối thiểu, MFA, user ID duy nhất |
| Giám sát | Ghi log, phát hiện xâm nhập |
| Chính sách bảo mật | Văn bản hóa, đào tạo định kỳ |
Các phương pháp bảo mật tốt nhất
// 1. Token hóa phía client - KHÔNG xử lý thẻ thô
const token = await tokenizeCard(cardData);
// 2. Dùng idempotency cho mọi giao dịch
const idempotencyKey = `pay_${orderId}_${Date.now()}`;
// 3. Xác thực số tiền phía server
if (req.body.amount !== calculatedAmount) {
throw new Error('Không khớp số tiền - có thể bị giả mạo');
}
// 4. Ghi log không chứa dữ liệu nhạy cảm
logger.info('Đã thử thanh toán', {
orderId,
amount,
currency,
customerId,
timestamp: new Date().toISOString()
// KHÔNG bao gồm số thẻ, CVV, v.v.
});
// 5. Dùng biến môi trường cho bí mật
const apiKey = process.env.IPAY_API_KEY;
// 6. Giới hạn tốc độ endpoint thanh toán
const paymentLimiter = rateLimit({
windowMs: 60000,
max: 10 // 10 lần/phút
});
Checklist triển khai production
Trước khi xử lý thanh toán thật, kiểm tra:
- [ ] Hoàn thành PCI DSS Self Assessment Questionnaire
- [ ] Tất cả endpoint dùng HTTPS
- [ ] Lưu khóa API trong secret manager an toàn
- [ ] Đã xác minh chữ ký webhook
- [ ] Thực hiện idempotency cho mọi thao tác thanh toán
- [ ] Ghi log toàn diện (không dữ liệu nhạy cảm)
- [ ] Kích hoạt anti-fraud rules
- [ ] Kiểm thử hoàn tiền & tranh chấp
- [ ] Có runbook xử lý lỗi thanh toán
- [ ] Giám sát & cảnh báo
- [ ] Có payment processor dự phòng
Các trường hợp sử dụng thực tế
Thanh toán thương mại điện tử
- Thách thức: Xử lý thanh toán thủ công, tỷ lệ bỏ giỏ hàng cao
- Giải pháp: One-page checkout với mã hóa token thẻ
- Kết quả: Tăng 35% conversion, thanh toán tức thì
Thanh toán đăng ký SaaS
- Thách thức: Lập hóa đơn, thu thập thủ công
- Giải pháp: Thanh toán định kỳ, tự động retry
- Kết quả: 95% thanh toán đúng hạn, giảm 80% thời gian quản trị
Ký quỹ thị trường
- Thách thức: Chia nhỏ thanh toán giữa nhiều nhà cung cấp
- Giải pháp: Ý định thanh toán + lập lịch chuyển khoản
- Kết quả: Thanh toán tự động cho vendor, giảm gian lận
Kết luận
Tích hợp API thanh toán đòi hỏi chú trọng bảo mật, tuân thủ và xử lý lỗi kỹ lưỡng. Tóm lại:
- Không bao giờ xử lý thẻ thô – luôn token hóa phía client
- Tất cả payment operation phải idempotent
- Xác minh chữ ký webhook để chống gian lận
- Phải tuân thủ PCI DSS
- Kiểm thử kỹ trên sandbox trước khi production
- Apidog giúp kiểm thử API và cộng tác nhóm hiệu quả
Phần Câu hỏi thường gặp (FAQ)
Làm cách nào để xác thực với API iPay?
Sử dụng Basic Auth với API key/secret, hoặc OAuth 2.0 cho ứng dụng đa tenant.
Tôi có thể lưu trữ chi tiết thẻ của khách hàng không?
Có, nhưng phải tuân thủ PCI DSS. Sử dụng token hóa để lưu thẻ an toàn trong vault của iPay.
Làm cách nào để xử lý thanh toán thất bại?
Xây dựng logic retry với exponential backoff, gửi thông báo cho khách và cho phép đổi phương thức thanh toán.
Tính bất biến (Idempotency) là gì và tại sao nó quan trọng?
Idempotency đảm bảo các request trùng key luôn ra cùng kết quả, tránh bị tính phí trùng lặp.
Làm cách nào để kiểm thử thanh toán mà không tính phí thẻ?
Dùng môi trường sandbox với số thẻ test, lấy trong tài liệu iPay.
Chữ ký webhook là gì?
Chữ ký HMAC dùng để xác minh webhook thực sự gửi từ iPay, không phải kẻ gian.
Top comments (0)