Skip to content

Latest commit

 

History

History
684 lines (549 loc) · 25.5 KB

File metadata and controls

684 lines (549 loc) · 25.5 KB

WidgetApi

You can use the APIs below to interface with DANA's WidgetApi API. To start using the API, you need to destruct instantiated DANA client. This client would be a singleton object that you can use across various api and operation.

import { Dana } from 'dana-node';

const danaClient = new Dana({
    partnerId: "YOUR_PARTNER_ID", // process.env.X_PARTNER_ID
    privateKey: "YOUR_PRIVATE_KEY", // process.env.X_PRIVATE_KEY
    origin: "YOUR_ORIGIN", // process.env.ORIGIN
    env: "sandbox", // process.env.DANA_ENV or process.env.ENV or "sandbox" or "production"
});
const { widgetApi } = danaClient;

Additional Documentation

All URIs are relative to https://api.saas.dana.id, except if the operation defines another base path (for sandbox it is http://api.sandbox.dana.id).

Method HTTP request Description
accountUnbinding POST /v1.0/registration-account-unbinding.htm This API is used to reverses the account binding process by revoking the accessToken and refreshToken
applyOTT POST /rest/v1.1/qr/apply-ott This API is used to get one time token that will be used as authorization parameter upon redirecting to DANA
applyToken POST /v1.0/access-token/b2b2c.htm This API is used to finalized account binding process by exchanging the authCode into accessToken that can be used as user authorization
balanceInquiry POST /v1.0/balance-inquiry.htm This API is used to query user's DANA account balance via merchant
cancelOrder POST /v1.0/debit/cancel.htm This API is used to cancel the order from merchant's platform to DANA
queryPayment POST /rest/v1.1/debit/status This API is used to inquiry payment status and information from merchant's platform to DANA
queryUserProfile POST /dana/member/query/queryUserProfile.htm The API is used to query user profile such as DANA balance (unit in IDR), masked DANA phone number, KYC or OTT (one time token) between merchant server and DANA's server
refundOrder POST /v1.0/debit/refund.htm This API is used to refund the order from merchant's platform to DANA
widgetPayment POST /rest/redirection/v1.0/debit/payment-host-to-host This API is used to initiate payment from merchant's platform to DANA

accountUnbinding() Function

Function Signature

Name Value
Function Name accountUnbinding
Request Parameters AccountUnbindingRequest
Return Type AccountUnbindingResponse

Usage Example

import { Dana } from 'dana-node';
import { AccountUnbindingRequest, AccountUnbindingResponse } from 'dana-node/widget/v1';

const danaClient = new Dana({
    partnerId: "YOUR_PARTNER_ID", // process.env.X_PARTNER_ID
    privateKey: "YOUR_PRIVATE_KEY", // process.env.X_PRIVATE_KEY
    origin: "YOUR_ORIGIN", // process.env.ORIGIN
    env: "sandbox", // process.env.DANA_ENV or process.env.ENV or "sandbox" or "production"
});
const { widgetApi } = danaClient;

const request: AccountUnbindingRequest = {
    // Define the request parameters for the API call here
};

const response: AccountUnbindingResponse = await widgetApi.accountUnbinding(request);

applyOTT() Function

Function Signature

Name Value
Function Name applyOTT
Request Parameters ApplyOTTRequest
Return Type ApplyOTTResponse

Usage Example

import { Dana } from 'dana-node';
import { ApplyOTTRequest, ApplyOTTResponse } from 'dana-node/widget/v1';

const danaClient = new Dana({
    partnerId: "YOUR_PARTNER_ID", // process.env.X_PARTNER_ID
    privateKey: "YOUR_PRIVATE_KEY", // process.env.X_PRIVATE_KEY
    origin: "YOUR_ORIGIN", // process.env.ORIGIN
    env: "sandbox", // process.env.DANA_ENV or process.env.ENV or "sandbox" or "production"
});
const { widgetApi } = danaClient;

const request: ApplyOTTRequest = {
    // Define the request parameters for the API call here
};

const response: ApplyOTTResponse = await widgetApi.applyOTT(request);

applyToken() Function

Function Signature

Name Value
Function Name applyToken
Request Parameters ApplyTokenRequest
Return Type ApplyTokenResponse

Usage Example

import { Dana } from 'dana-node';
import { ApplyTokenRequest, ApplyTokenResponse } from 'dana-node/widget/v1';

const danaClient = new Dana({
    partnerId: "YOUR_PARTNER_ID", // process.env.X_PARTNER_ID
    privateKey: "YOUR_PRIVATE_KEY", // process.env.X_PRIVATE_KEY
    origin: "YOUR_ORIGIN", // process.env.ORIGIN
    env: "sandbox", // process.env.DANA_ENV or process.env.ENV or "sandbox" or "production"
});
const { widgetApi } = danaClient;

const request: ApplyTokenRequest = {
    // Define the request parameters for the API call here
};

const response: ApplyTokenResponse = await widgetApi.applyToken(request);

balanceInquiry() Function

Function Signature

Name Value
Function Name balanceInquiry
Request Parameters BalanceInquiryRequest
Return Type BalanceInquiryResponse

Usage Example

import { Dana } from 'dana-node';
import { BalanceInquiryRequest, BalanceInquiryResponse } from 'dana-node/widget/v1';

const danaClient = new Dana({
    partnerId: "YOUR_PARTNER_ID", // process.env.X_PARTNER_ID
    privateKey: "YOUR_PRIVATE_KEY", // process.env.X_PRIVATE_KEY
    origin: "YOUR_ORIGIN", // process.env.ORIGIN
    env: "sandbox", // process.env.DANA_ENV or process.env.ENV or "sandbox" or "production"
});
const { widgetApi } = danaClient;

const request: BalanceInquiryRequest = {
    // Define the request parameters for the API call here
};

const response: BalanceInquiryResponse = await widgetApi.balanceInquiry(request);

cancelOrder() Function

Function Signature

Name Value
Function Name cancelOrder
Request Parameters CancelOrderRequest
Return Type CancelOrderResponse

Usage Example

import { Dana } from 'dana-node';
import { CancelOrderRequest, CancelOrderResponse } from 'dana-node/widget/v1';

const danaClient = new Dana({
    partnerId: "YOUR_PARTNER_ID", // process.env.X_PARTNER_ID
    privateKey: "YOUR_PRIVATE_KEY", // process.env.X_PRIVATE_KEY
    origin: "YOUR_ORIGIN", // process.env.ORIGIN
    env: "sandbox", // process.env.DANA_ENV or process.env.ENV or "sandbox" or "production"
});
const { widgetApi } = danaClient;

const request: CancelOrderRequest = {
    // Define the request parameters for the API call here
};

const response: CancelOrderResponse = await widgetApi.cancelOrder(request);

queryPayment() Function

Function Signature

Name Value
Function Name queryPayment
Request Parameters QueryPaymentRequest
Return Type QueryPaymentResponse

Usage Example

import { Dana } from 'dana-node';
import { QueryPaymentRequest, QueryPaymentResponse } from 'dana-node/widget/v1';

const danaClient = new Dana({
    partnerId: "YOUR_PARTNER_ID", // process.env.X_PARTNER_ID
    privateKey: "YOUR_PRIVATE_KEY", // process.env.X_PRIVATE_KEY
    origin: "YOUR_ORIGIN", // process.env.ORIGIN
    env: "sandbox", // process.env.DANA_ENV or process.env.ENV or "sandbox" or "production"
});
const { widgetApi } = danaClient;

const request: QueryPaymentRequest = {
    // Define the request parameters for the API call here
};

const response: QueryPaymentResponse = await widgetApi.queryPayment(request);

queryUserProfile() Function

Function Signature

Name Value
Function Name queryUserProfile
Request Parameters QueryUserProfileRequest
Return Type QueryUserProfileResponse

Usage Example

import { Dana } from 'dana-node';
import { QueryUserProfileRequest, QueryUserProfileResponse } from 'dana-node/widget/v1';

const danaClient = new Dana({
    partnerId: "YOUR_PARTNER_ID", // process.env.X_PARTNER_ID
    privateKey: "YOUR_PRIVATE_KEY", // process.env.X_PRIVATE_KEY
    origin: "YOUR_ORIGIN", // process.env.ORIGIN
    env: "sandbox", // process.env.DANA_ENV or process.env.ENV or "sandbox" or "production"
});
const { widgetApi } = danaClient;

const request: QueryUserProfileRequest = {
    // Define the request parameters for the API call here
};

const response: QueryUserProfileResponse = await widgetApi.queryUserProfile(request);

refundOrder() Function

Function Signature

Name Value
Function Name refundOrder
Request Parameters RefundOrderRequest
Return Type RefundOrderResponse

Usage Example

import { Dana } from 'dana-node';
import { RefundOrderRequest, RefundOrderResponse } from 'dana-node/widget/v1';

const danaClient = new Dana({
    partnerId: "YOUR_PARTNER_ID", // process.env.X_PARTNER_ID
    privateKey: "YOUR_PRIVATE_KEY", // process.env.X_PRIVATE_KEY
    origin: "YOUR_ORIGIN", // process.env.ORIGIN
    env: "sandbox", // process.env.DANA_ENV or process.env.ENV or "sandbox" or "production"
});
const { widgetApi } = danaClient;

const request: RefundOrderRequest = {
    // Define the request parameters for the API call here
};

const response: RefundOrderResponse = await widgetApi.refundOrder(request);

widgetPayment() Function

Function Signature

Name Value
Function Name widgetPayment
Request Parameters WidgetPaymentRequest
Return Type WidgetPaymentResponse

Usage Example

import { Dana } from 'dana-node';
import { WidgetPaymentRequest, WidgetPaymentResponse } from 'dana-node/widget/v1';

const danaClient = new Dana({
    partnerId: "YOUR_PARTNER_ID", // process.env.X_PARTNER_ID
    privateKey: "YOUR_PRIVATE_KEY", // process.env.X_PRIVATE_KEY
    origin: "YOUR_ORIGIN", // process.env.ORIGIN
    env: "sandbox", // process.env.DANA_ENV or process.env.ENV or "sandbox" or "production"
});
const { widgetApi } = danaClient;

const request: WidgetPaymentRequest = {
    // Define the request parameters for the API call here
};

const response: WidgetPaymentResponse = await widgetApi.widgetPayment(request);

Enum Types

In Node.js, enums are located within each model class rather than being centralized in a separate enum file. Each enum is named after its parent model.

For example, to use the promoType enum from PaymentPromoInfo:

import { PaymentPromoInfoPromoTypeEnum } from 'dana-node/widget/v1';

// Use the enum value
const promoType = PaymentPromoInfoPromoTypeEnum.CashBack;

In this example the PaymentPromoInfo is the parent model and PromoType is the enum name. In below list, the enums are listed in format of {ParentModel}{EnumName} (Enum Field).

ApplyOTTRequestUserResourcesEnum (userResources)

Value Description
Ott

ApplyTokenAuthorizationCodeRequestGrantTypeEnum (grantType)

Value Description
AuthorizationCode

ApplyTokenRefreshTokenRequestGrantTypeEnum (grantType)

Value Description
RefreshToken

EnvInfoSourcePlatformEnum (sourcePlatform)

Value Description
Ipg The source platform is always independent payment gateway (IPG)

EnvInfoTerminalTypeEnum (terminalType)

Value Description
App Mobile Application
Web Browser Web
Wap Mobile Wap
System System Call

EnvInfoOrderTerminalTypeEnum (orderTerminalType)

Value Description
App Mobile Application
Web Browser Web
Wap Mobile Wap
System System Call

Oauth2UrlDataModeEnum (mode)

Value Description
Api
Deeplink

PayOptionInfoPayMethodEnum (payMethod)

Value Description
Balance Payment method with balance
Coupon Payment method with coupon
NetBanking Payment method with internet banking
CreditCard Payment method with credit card
DebitCard Payment method with debit card
VirtualAccount Payment method with virtual account
Otc Payment method with OTC
DirectDebitCreditCard Payment method with direct debit of credit card
DirectDebitDebitCard Payment method with direct debit of debit card
OnlineCredit Payment method with online Credit
LoanCredit Payment method with DANA Cicil
NetworkPay Payment method with e-wallet
Card Payment method with Card

PayOptionInfoPayOptionEnum (payOption)

Value Description
NetworkPayPgSpay Payment method with ShopeePay e-wallet
NetworkPayPgOvo Payment method with OVO e-wallet
NetworkPayPgGopay Payment method with GoPay e-wallet
NetworkPayPgLinkaja Payment method with LinkAja e-wallet
NetworkPayPgCard Payment method with Card
NetworkPayPcIndomaret Payment method with Indomaret
NetworkPayPgQris Payment method with QRIS
NetworkPayPlIndodana Payment method with Paylater Indodana
VirtualAccountBca Payment method with BCA virtual account
VirtualAccountBni Payment method with BNI virtual account
VirtualAccountMandiri Payment method with Mandiri virtual account
VirtualAccountBri Payment method with BRI virtual account
VirtualAccountBtpn Payment method with BTPN virtual account
VirtualAccountCimb Payment method with CIMB virtual account
VirtualAccountPermata Payment method with Permata virtual account
VirtualAccountPani Payment method with Panin virtual account
VirtualAccountBsiPayment

PaymentPromoInfoPromoTypeEnum (promoType)

Value Description
CashBack
Discount
Voucher
Point

QueryUserProfileRequestUserResourcesEnum (userResources)

Value Description
Balance
TopupUrl
TransactionUrl
Ott
MaskDanaId
UserKyc
LoginId
ClearTextDanaId
Nickname
Fullname
KtpNumber
KtpPhotoData
SelfiePhotoData
AvatarUrl
MaskedFullname

QueryUserProfileResponseResponseHeadFunctionEnum (function)

Value Description
Dana.member.query.queryuserprofile

RefundPromoInfoPromoTypeEnum (promoType)

Value Description
CashBack
Discount
Voucher
Point

ResultInfoResultStatusEnum (resultStatus)

Value Description
S Success
F Failure
U Unknown

ServiceInfoServiceTypeEnum (serviceType)

Value Description
Parking
Investment

ServiceInfoServiceScenarioEnum (serviceScenario)

Value Description
ScanAndPay
ExitAndPay
EmasPurchase

StatusDetailAcquirementStatusEnum (acquirementStatus)

Value Description
Init Order is created but not paid yet
Success Order is succeeded
Closed Order is closed
Paying Order is paid but not finish
MerchantAccept Order is accepted by merchant after order is paid for PAY-CONFIRM
Cancelled Order is cancelled

UrlParamTypeEnum (type)

Value Description
PayReturn After the payment, the user will be redirected to merchant page, this is mandatory
Notification When finish payment, DANA will notify to the URL that has been defined by user

UserResourceInfoResourceTypeEnum (resourceType)

Value Description
Balance Query balance of user in DANA
TopupUrl Obtain the top up URL for merchant to redirect
TransactionUrl Obtain the transaction URL for merchant to redirect
Ott Obtain the OTT of URLs including TOPUP/TRANSACTION/CASHIER/CHECKOUT_URL
MaskDanaId The masked identifier from DANA side
UserKyc KYC level. 00 = KYC level 0, 02 = KYC level 2
LoginId Login identifier of the user, currently it's only set to phone number
ClearTextDanaId The unmasked identifier from DANA side
Nickname Nickname of the user in DANA
Fullname Full name of the user in DANA
KtpNumber KTP number of the user in DANA
KtpPhotoData KTP photo binary data in base64 of the user in DANA
SelfiePhotoData Selfie photo binary data in base64 of the user in DANA
AvatarUrl Location of avatar photo of the user in DANA
MaskedFullname Masked full name of the user in DANA

Webhook Verification

This document explains how to use the WebhookParser utility from the `` SDK to securely verify and parse webhook notifications sent by DANA.

Example

import { WebhookParser } from 'dana-node/webhook/v1'; // Adjust import path as needed
// Assuming you are in an Express.js route handler or similar framework context.
// If using Express, you might import types like this:
// import { Request, Response } from 'express';

// Example route handler for DANA webhooks.
// Replace `AnyRequestType` and `AnyResponseType` with types from your web framework (e.g., Express's Request, Response).
async function handleDanaWebhook(req: AnyRequestType, res: AnyResponseType) {
    // Retrieve the DANA public key from environment variables or a secure configuration.
    // Option 1: Public key as a string
    const danaPublicKeyString: string | undefined = process.env.DANA_PUBLIC_KEY;
    // Option 2: Path to the public key file (recommended for production)
    const danaPublicKeyPath: string | undefined = process.env.DANA_PUBLIC_KEY_PATH;

    if (!danaPublicKeyString && !danaPublicKeyPath) {
        console.error('DANA webhook public key not configured.');
        res.status(500).send('Webhook processor configuration error.'); // Or appropriate error handling
        return;
    }

    // Extract necessary data from the request object
    const httpMethod: string = req.method!; // e.g., "POST"
    const relativePathUrl: string = req.path!; // e.g., "/v1.0/debit/notify". Ensure this is the path DANA signs.

    // req.headers is typically an object like { 'header-name': 'value' }.
    // The WebhookParser expects Record<string, string>.
    // Node's IncomingHttpHeaders can have string[] for values, but X-SIGNATURE and X-TIMESTAMP
    // are expected to be single strings. Casting or careful handling might be needed.
    const headers: Record<string, string> = req.headers as Record<string, string>;

    // The body needs to be the raw JSON string.
    // If you're using a body-parsing middleware (e.g., express.json()),
    // req.body might already be a parsed JavaScript object.
    // The WebhookParser expects the raw string body that was signed.
    let requestBodyString: string;
    if (typeof req.body === 'string') {
        requestBodyString = req.body;
    } else if (req.body && typeof req.body === 'object') {
        // If req.body is an object, it means middleware parsed it.
        // We need to stringify it back to pass to the parser,
        // assuming this stringified version matches what DANA signed.
        // IMPORTANT: This assumes JSON.stringify(parsedBody) is equivalent to the raw body DANA sent.
        // For robust parsing, it's best to get the raw body *before* any JSON parsing middleware,
        // e.g., using `express.raw({ type: 'application/json' })` for specific routes.
        requestBodyString = JSON.stringify(req.body);
    } else {
        console.error('Request body is not a string or a parseable object.');
        res.status(400).send('Invalid request body format.');
        return;
    }

    // Initialize WebhookParser.
    // The constructor prioritizes publicKeyPath if both are provided.
    const parser = new WebhookParser(danaPublicKeyString, danaPublicKeyPath);

    try {
        // Verify the signature and parse the webhook payload
        const finishNotify = parser.parseWebhook(
            httpMethod,
            relativePathUrl,
            headers,
            requestBodyString
        );

        console.log('Webhook verified successfully:');
        console.log('Original Partner Reference No:', finishNotify.originalPartnerReferenceNo);
        // TODO: Process the finishNotify object (e.g., update order status in your database)

        res.status(200).send('Webhook received and verified.');
    } catch (error: any) { // Catching as 'any' to access error.message
        console.error('Webhook verification failed:', error.message);
        // Respond with an error status. DANA might retry if it receives an error.
        res.status(400).send(`Webhook verification failed: ${error.message}`);
    }
}

API Reference

WebhookParser

Constructor:

new WebhookParser(publicKey?: string, publicKeyPath?: string)
  • publicKey (string, optional): The DANA gateway's public key as a PEM formatted string. This is used if publicKeyPath is not provided or is empty.
  • publicKeyPath (string, optional): The file path to the DANA gateway's public key PEM file. If provided, this will be prioritized over the publicKey string.

One of publicKey or publicKeyPath must be provided.

Method:

parseWebhook(httpMethod: string, relativePathUrl: string, headers: { [key: string]: string }, body: string): FinishNotifyRequest
  • http_method: HTTP method of the webhook request (e.g., POST).
  • relative_path_url: The relative URL path (e.g., /v1.0/debit/notify).
  • headers: Dictionary containing at least X-SIGNATURE and X-TIMESTAMP.
  • body: Raw JSON string of the webhook payload.
  • Returns: FinishNotifyRequest model with parsed data.
  • Raises: ValueError if signature verification fails or the payload is invalid.

Security Notes

  • Always use the official public key provided by DANA for webhook verification.
  • Reject any webhook requests that fail signature verification or have malformed payloads.
  • Never trust webhook data unless it passes verification.

Webhook Notification Models

The following webhook notification models may be received:

Model Description
FinishNotifyRequest Represents the standard notification payload for payment events.

OAuth URL Generation

You can generate OAuth URLs for widget authorization using the WidgetUtils helper:

import { WidgetUtils, Oauth2UrlDataModeEnum } from 'dana-node/widget/v1';

// Generate OAuth URL
const oauth2UrlData = {
    redirectUrl: 'https://your-redirect-url.com',
    externalId: 'your-external-id', // or use uuidv4()
    merchantId: process.env.MERCHANT_ID,
    seamlessData: {
        mobileNumber: '08xxxxxxxxx' // Optional
    },
    mode: Oauth2UrlDataModeEnum.Api // or Oauth2UrlDataModeEnum.Deeplink
};

const oauthUrl = WidgetUtils.generateOauthUrl(oauth2UrlData);
console.log(oauthUrl);

The generated URL can be used to redirect users to DANA's authorization page.

Complete Payment URL Generation

You can generate a payment URL by combining the webRedirectUrl from a WidgetPaymentResponse with an OTT token from an ApplyOTTResponse.

import { Util } from 'dana-node/widget/v1';
import { WidgetPaymentResponse, ApplyOTTResponse } from 'dana-node/widget/v1/models';

// Example response from createWidgetPayment
const widgetPaymentResponse = new WidgetPaymentResponse({
  webRedirectUrl: 'https://example.com/payment?token=abc123'
}); // this should be from createPayment Widget API

// Example response from applyOTT
const applyOTTResponse = new ApplyOTTResponse({
  userResources: [
    {
      value: 'ott_token_value'
    }
  ]
}); // this should be from applyOTT Widget API

// Generate the payment URL
const paymentUrl = Util.generateCompletePaymentUrl(widgetPaymentResponse, applyOTTResponse);

The payment URL can be used to pay the widget