Nequi
Nequiとの連携を開始する#
このページは、DEUNAとのNequiの統合を成功させるための包括的なガイドを提供します。
Nequiは、Bancolombiaによって管理されているデジタルウォレットであり、コロンビアで広く利用されています。
仕組み#
DEUNAとNequiの統合により、ユーザーはDEUNAの決済ウィジェット内で完全に処理されるプッシュ通知フローを通じて支払うことができます。外部へのリダイレクトは不要です。
Proceso de pago
Следующий список описывает действительный процесс оплаты с помощью Nequi:
- ユーザーは決済ウィジェット内でNequiを選択します。
- El usuario ingresa su número de teléfono real de Nequi.
- Nequiモバイルアプリにプッシュ通知が送信されます。
- ユーザーはアプリ内で直接支払いを承認します。
- DEUNAは支払いステータスを受け取ります。
- DEUNAはウィジェットで適切なコールバックイベントをトリガーします:
onSuccess承認された支払いの場合onError拒否または期限切れの場合
onErrorイベントの処理
DEUNAのウィジェットは、以下の処理を開始します。 onError コールバックの実行は、以下のシナリオにおいて:
- チェックアウトフォームに提供された電話番号は有効ですが、Nequiアカウントに関連付けられていません。
- ユーザーがNequiアプリからのプッシュ通知を拒否する。
いずれの場合も、DEUNAはエラーコードとエラーメッセージを含むレスポンスを返します。このレスポンスは、ビジネスロジックとユーザーエクスペリエンスに基づいて処理できます。
要件#
以下の内容には、Nequiとの成功的な統合に必要な要件がすべて記載されています。
Nequiのサンドボックスおよび本番環境の認証情報は、BancolombiaまたはNequiの担当者から直接取得する必要があります。
環境で使用:
- サンドボックス: https://api.sandbox.deuna.io
- 本番環境: https://api.deuna.io
Nequiとの認証プロセスを完了し、Nequiから提供される以下の認証情報を取得してください。
-
Merchant ID
-
商用利用パスワード
-
公開鍵
-
識別コード
-
有効期限(最大45分)
NequiでのWebhookの設定#
各店舗で、Nequiセッションにウェブフックを設定してください。
Nequiのウェブフックを設定しない場合、Nequiでのステータス更新には2~5分程度の遅延が発生します。
ウェブフックのエンドポイントは、高い可用性とスケーラビリティを備えていることを確認してください。 必要に応じて、サービス利用不可による支払い確認失敗時に、取引の取り消しを設定できます。
ウェブフックペイロード
Nequiは、以下のJSONペイロードを送信します:
{
"commerceCode": "29603",
"value": "1",
"phoneNumber": "3195414070",
"messageId": "60396545535",
"transactionId": "350-12345-34000201-60396545535",
"region": "C001",
"receivedAt": "2023-02-27T15:50:13.527Z",
"paymentStatus": "SUCCESS"
}ペイロードフィールド
| フィールド | 説明 |
|---|---|
commerceCode | あなたの内部のNequi商社コード |
value | 支払い金額 |
phoneNumber | 支払いの担当者のモバイル電話番号 |
messageId | 一意の取引識別子 |
transactionId | 支払い識別子 |
region | 支払い地域: P001 (パナマ) または C001 (コロンビア) |
receivedAt | 支払い日時をJSON形式で |
paymentStatus | 支払いステータス: SUCCESS, CANCELED、または DENIED |
セキュリティ要件の検証
NequiからのすべてのWebhookリクエストには、検証が必要なセキュリティヘッダーが含まれています:
リクエストヘッダー
{
"Content-Type": "application/json",
"Digest": "SHA-256=43GpOk5L54gfpAMBE0xNX1bj2hJA9JJ1RR0dErHfZhI=",
"Signature": "keyId=\"YourClientId\",algorithm=\"hmac-sha384\",headers=\"content-type digest\",signature=\"...\""
}検証プロセス
各リクエストについて、以下の2つの項目を確認する必要があります。
1. ボディのダイジェストの検証
次の Digest ヘッダーには、リクエストボディのSHA-256ハッシュが含まれています:
// Convert body to string
const parsedBody = JSON.stringify(request.body);
// Calculate SHA-256 hash
const calculatedDigest = `SHA-256=${createHash('sha256')
.update(parsedBody)
.digest('base64')}`;
// Verify it matches the header
if (calculatedDigest !== request.headers.Digest) {
return 401; // Invalid Digest
}2. リクエストの署名の検証
次の Signature ヘッダーは、リクエストがNequiから送信されたことを保証します:
- 署名ヘッダーを解析します。
const parts = request.headers.Signature.split(',');
const signature = {};
for (const part of parts) {
const [key, value] = part.split('=');
signature[key] = value.slice(1, -1); // Remove quotes
}
// Result:
// {
// keyId: "YourClientId",
// algorithm: "hmac-sha384",
// headers: "content-type digest",
// signature: "..."
// }- 署名テキストを構築します。
const headerNames = signature.headers.split(' '); // ["content-type", "digest"]
const linesForSignature = [];
for (const headerName of headerNames) {
const value = request.headers[headerName.toLowerCase()];
linesForSignature.push(`${headerName}: ${value}`);
}
const textForSignature = linesForSignature.join('\n');
// Result:
// "content-type: application/json
// digest: SHA-256=43GpOk5L54gfpAMBE0xNX1bj2hJA9JJ1RR0dErHfZhI="- HMACを計算し、検証します。
const APP_SECRET = 'YourSharedSecret'; // Store securely, never hardcode
// Calculate HMAC-SHA384
const base64hmac = createHmac('sha384', APP_SECRET)
.update(textForSignature)
.digest('base64');
// Convert to base64url format
const calculatedSignature = base64hmac
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
// Verify signature
if (calculatedSignature !== signature.signature) {
return 401; // Invalid Signature
}完全な実装例 (Node.js)
const { createHash, createHmac } = require('node:crypto');
// Store securely - use environment variables
const APP_SECRET = process.env.NEQUI_WEBHOOK_SECRET;
async function handleNequiWebhook(request, response) {
try {
// 1. Verify Body Digest
const parsedBody = JSON.stringify(request.body);
const calculatedDigest = `SHA-256=${createHash('sha256')
.update(parsedBody)
.digest('base64')}`;
if (calculatedDigest !== request.headers.digest) {
return response.status(401).send('Invalid Digest');
}
// 2. Parse Signature Header
const parts = request.headers.signature.split(',');
const signatureData = {};
for (const part of parts) {
const [key, value] = part.split('=');
signatureData[key] = value.slice(1, -1);
}
// 3. Build Signing Text
const headerNames = signatureData.headers.split(' ');
const linesForSignature = headerNames.map(name =>
`${name}: ${request.headers[name.toLowerCase()]}`
);
const textForSignature = linesForSignature.join('\n');
// 4. Verify Signature
const base64hmac = createHmac('sha384', APP_SECRET)
.update(textForSignature)
.digest('base64');
const calculatedSignature = base64hmac
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
if (calculatedSignature !== signatureData.signature) {
return response.status(401).send('Invalid Signature');
}
// 5. Process the payment notification asynchronously
processPaymentAsync(request.body);
// 6. Respond immediately (within 10 seconds)
return response.status(200).send('OK');
} catch (error) {
console.error('Webhook processing error:', error);
return response.status(500).send('Internal Server Error');
}
}
async function processPaymentAsync(paymentData) {
// Process payment in background
// Update your database, trigger notifications, etc.
const { transactionId, paymentStatus, value, phoneNumber } = paymentData;
if (paymentStatus === 'SUCCESS') {
// Handle successful payment
} else if (paymentStatus === 'CANCELED' || paymentStatus === 'REFUSED') {
// Handle failed payment
}
}Nequiとの統合#
技術要件が設定されたら、段階的な統合を開始できます。
1. 次の操作を実行してください。 order_token
購入を行うには、DEUNA の注文を作成する必要があります。
V2の「Purchase」エンドポイントへのリクエストを送信してください。 注文の作成 endpoint.
API は、 order_tokenを返します。これは、全体のワークフローで使用されます。
2. 次の情報を取得してください。 order_token
V2の「Purchase」エンドポイントへのリクエストを送信してください。 Get Order endpoint.
このトークンを、次の手順で使用してください。
3. 決済ウィジェットの表示
注文トークンを受け取った後、以下の処理を実行できます。 DEUNAウィジェット.
await DeunaSDK.initPaymentWidget({
orderToken: "<DEUNA order token>",
callbacks: ...,
paymentMethods: [
{
paymentMethod: "voucher",
processors: ["nequi_push_voucher"],
},
],
});4. クレジットカードによる支払い
カード決済リクエストを Purchase V2 エンドポイントとプロセスを実行します。
応答
{
"order": {
"cash_change": 0,
"currency": "USD",
"discounts": [],
"display_items_total_amount": "",
"display_shipping_amount": "",
"display_sub_total": "",
"display_tax_amount": "",
"display_total_amount": "",
"display_total_discount": "",
"gift_card": [],
"items": [
{
"brand": "",
"category": "",
"color": "",
"description": "Papa Fritas",
"details_url": "",
"discounts": [],
"id": "001",
"image_url": "https://images-staging.getduna.com/95463fb5-6279-4ec3-8ff9-fe07aacd2142/cd928351d12c6b96_domicilio_316_744x744.png?d=600x600",
"isbn": "",
"manufacturer": "",
"name": "Papa Fritas",
"options": "",
"quantity": 1,
"size": "",
"sku": "",
"tax_amount": {
"amount": 44,
"currency": "USD",
"currency_symbol": "$",
"display_amount": ""
},
"taxable": false,
"total_amount": {
"amount": 594,
"currency": "USD",
"currency_symbol": "$",
"display_amount": "",
"display_original_amount": "",
"display_total_discount": "",
"original_amount": 0,
"total_discount": 0
},
"type": "",
"unit_price": {
"amount": 550,
"currency": "USD",
"currency_symbol": "$",
"display_amount": ""
},
"uom": "",
"upc": "",
"weight": {
"unit": "",
"weight": 0
}
},
{
"brand": "",
"category": "",
"color": "",
"description": "Hamburguesa ",
"details_url": "",
"discounts": [],
"id": "002",
"image_url": "https://images-staging.getduna.com/95463fb5-6279-4ec3-8ff9-fe07aacd2142/cd928351d12c6b96_domicilio_51330_744x744_1646338877.png?d=600x600",
"isbn": "",
"manufacturer": "",
"name": "Hamburguesa",
"options": "",
"quantity": 2,
"size": "",
"sku": "",
"tax_amount": {
"amount": 88,
"currency": "USD",
"currency_symbol": "$",
"display_amount": ""
},
"taxable": false,
"total_amount": {
"amount": 3088,
"currency": "USD",
"currency_symbol": "$",
"display_amount": "",
"display_original_amount": "",
"display_total_discount": "",
"original_amount": 0,
"total_discount": 0
},
"type": "",
"unit_price": {
"amount": 1500,
"currency": "USD",
"currency_symbol": "$",
"display_amount": ""
},
"uom": "",
"upc": "",
"weight": {
"unit": "",
"weight": 0
}
}
],
"items_total_amount": 3682,
"metadata": {
"key1": "NO REQUERIDO",
"key2": "NO REQUERIDO"
},
"order_id": "order116",
"payment": {
"data": {
"amount": {
"amount": 3770,
"currency": "USD"
},
"authorization_3ds": {
"html_content": "<div></div>",
"url_challenge": "",
"version": "1.1.1"
},
"authorization_code": "TEST00",
"created_at": "2022-07-22 20:22:48.408419489 +0000 UTC",
"customer": {
"email": "jhondoe@deuna.com",
"id": "xxxxxx-0eb3-450f-8b26-4ff23208470f"
},
"from_card": {
"card_brand": "Visa",
"first_six": "411111",
"last_four": "1111"
},
"id": "order116",
"installments": {
"installment_amount": 5999,
"installment_rate": 0.12,
"installment_type": "MCI",
"installments": 3,
"plan_id": "7471ed27-094d-44c4-a62d-225644b782f7",
"plan_option_id": "87309ea8-3942-4fdf-95ec-ce29a792aff2"
},
"merchant": {
"id": "9a85e296-cc3d-454b-b591-208d6e538126",
"store_code": "all"
},
"metadata": {
"authorization_code": "TEST00"
},
"method_type": "credit_card",
"processor": "paymentez",
"reason": "",
"status": "processed",
"updated_at": "2022-07-22 20:22:48.408809765 +0000 UTC"
}
},
"redirect_url": "",
"scheduled_at": "",
"shipping": null,
"shipping_address": {
"additional_description": "Piso 9",
"address_type": "home",
"address1": "Av. de los Incas 15-33, Ambato 180202, Ecuador",
"address2": "Av. de los Incas 15-33, Ambato 180202, Ecuador",
"city": "Ambato",
"country_code": "EC",
"created_at": "0001-01-01T00:00:00Z",
"first_name": "Jhon",
"id": 0,
"identity_document": "146565656",
"is_default": true,
"last_name": "Doe",
"lat": -1.2480678792202227,
"lng": -78.62532788804577,
"phone": " 946565665",
"state_name": "Tungurahua",
"updated_at": "0001-01-01T00:00:00Z",
"user_id": "xxxxx4e2-xxxx-xxxx-xxxx-xxxxx5b7b2e",
"zipcode": "180202"
},
"shipping_amount": 0,
"shipping_method": null,
"shipping_methods": [],
"shipping_options": {},
"status": "succeeded",
"store_code": "",
"sub_total": 3550,
"tax_amount": 132,
"timezone": "",
"total_amount": 3770,
"total_discount": 0,
"user_instructions": "Piso 9",
"webhook_urls": null
},
"order_token": "0b98dbe8-d265-49bc-b80d-536cea46509c"
}Nequiをテストする#
Nequiをテストするには、Nequi用のテストデータを必要とします。必要に応じて、DEUNA Admin Panelまたは リバンドAPI.