﻿# Drop in - CARD

## 1. Interaction Process

```mermaid
%%{init: {
  'theme': 'base',
  'themeVariables': {
    'primaryColor': '#e6f0ff',
    'primaryTextColor': '#333',
    'primaryBorderColor': '#5b9bd5',
    'lineColor': '#888',
    'actorMargin': 40,
    'noteBkgColor': '#0056b3',
    'noteTextColor': '#ffffff',
    'noteBorderColor': '#004a99'
  }
}}%%
sequenceDiagram
    participant User as User
    participant Page as Merchant Page
    participant Component as PayerMax
Drop-in Component
    participant MServer as Merchant Server
    participant PMServer as PayerMax Server
    participant Channel as Payment Channel
Wallet/Bank, etc.

    %% 1. Initialization Phase
    User->>Page: 1.1 Select product and place order
    Page->>MServer: 1.2 Send order information
e.g., Country, Currency, etc.
    MServer->>PMServer: 1.3 Get Drop-in initialization info
clientKey and sessionKey
    PMServer-->>MServer: 1.4 Return results
clientKey and sessionKey
    MServer-->>Page: 1.5 Return results
including clientKey and sessionKey
    Page->>Component: 1.6 Create and mount PayerMax component

    %% 2. Token Acquisition Phase
    User->>Page: 2.1 User enters payment information
    Page->>Component: 2.2 Request paymentToken
    Component->>PMServer: 2.3 Get paymentToken
    PMServer-->>Component: 2.4 Return result
including paymentToken
    Component-->>Page: Return paymentToken

    %% 3. Order Submission and Payment
    User->>Page: 3.1 Confirm payment
    Page->>Component: 3.2 Submit payment
including paymentToken
    Component->>MServer: 3.3 Submit order
including paymentToken
    MServer->>PMServer: 3.4 Create payment
Call Drop-in checkout API
    PMServer->>Channel: 3.5 Payment request
    PMServer-->>MServer: 3.6 Return result
    MServer-->>Component: 3.7 Return result
    Component-->>Page: 3.8 Return result

    %% 4. Obtain Payment Result (Logic Box)
    rect rgb(235, 245, 255)
        Note over MServer, PMServer: Obtain Payment Result
        
        Note over MServer, PMServer: Via Payment Result Notification
        PMServer->>MServer: 4.1 Asynchronous notification of payment result
        MServer->>MServer: 4.2 Update payment status
        MServer-->>PMServer: 4.3 Return response

        Note over MServer, PMServer: Via Payment Order Query
        MServer->>PMServer: 6.1 Query payment transaction
        PMServer-->>MServer: 6.2 Transaction details, including payment result
        MServer->>MServer: 6.3 Update payment status
    end
```

## 2. API Introduction

### 2.1 Interface List

| Related Interaction Sequence                                 | Call Direction           | API Type      | API PATH                                                                                                                                                                                                                                                                                                                                       |
| ------------------------------------------------------------ | ------------------------ | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 3.1 Obtain hosted component initialization information       | `Merchant` -> `PayerMax` | `Backend API` | [/applyDropinSession]([https://docs.payermax.com/api.html?docName=New%20Version&docVer=v1.0&docLang=cn#/paths/aggregate-pay-api-gateway-applyDropinSession/post.html](https://docs.payermax.com/api.html?docName=New%20Version&docVer=v1.0&docLang=cn#/paths/aggregate-pay-api-gateway-applyDropinSession/post.html))                          |
| 3.3 Create payment, call hosted component order creation API | `Merchant` -> `PayerMax` | `Backend API` | [/orderAndPay]([https://docs.payermax.com/api.html?docName=New%20Version&docVer=v1.0&docLang=cn#/paths/aggregate-pay-api-gateway-orderAndPay(for-drop-dont-copy-me)/post.html](https://docs.payermax.com/api.html?docName=New%20Version&docVer=v1.0&docLang=cn#/paths/aggregate-pay-api-gateway-orderAndPay(for-drop-dont-copy-me)/post.html)) |
| 3.4.1 Asynchronous notification of payment results           | `PayerMax` -> `Merchant` | `Backend API` | [/collectResultNotifyUrl]([https://docs.payermax.com/api.html?docName=New%20Version&docVer=v1.0&docLang=cn#/paths/collectResultNotifyUrl/post.html](https://docs.payermax.com/api.html?docName=New%20Version&docVer=v1.0&docLang=cn#/paths/collectResultNotifyUrl/post.html))                                                                  |
| 3.4.2 Query payment transaction                              | `Merchant` -> `PayerMax` | `Backend API` | [/orderQuery]([https://docs.payermax.com/api.html?docName=New%20Version&docVer=v1.0&docLang=cn#/paths/aggregate-pay-api-gateway-orderQuery/post.html](https://docs.payermax.com/api.html?docName=New%20Version&docVer=v1.0&docLang=cn#/paths/aggregate-pay-api-gateway-orderQuery/post.html))                                                  |

### 2.2 Environmental Information

- **Test environment request address**：https:// `pay-gate-uat.payermax.com`/aggregate-pay/api/gateway/ `<Interface PATH>`

- **Integrated Environment**：https:// `pay-gate.payermax.com`/aggregate-pay/api/gateway/ `<Interface PATH>`

### 2.3 Request header

``` js
"headers": {
"Accept": "application/json",
"sign": "Please refer to the signing rules: https://docs-v2.payermax.comen/202506-version/developer/config-settings.html", // Required
"Content-Type": "application/json"
}
```
## 3.Start Integration
### 3.1 Getting Drop in Initialization Information

The merchant server through the [/applyDropinSession API](https://docs.payermax.com/api.html?docName=New%20Version&docVer=v1.0&docLang=cn#/paths/aggregate-pay-api-gateway-applyDropinSession/post.html) interface, initiates an HTTP POST request to obtain the client token `clientKey` and the session token `sessionKey` required for the initialization of the drop in.

[/applyDropinSession API](https://docs.payermax.com/api.html?docName=New%20Version&docVer=v1.0&docLang=cn#/paths/aggregate-pay-api-gateway-applyDropinSession/post.html) Example of an interface request:

``` json
{
    "requestTime": "2024-11-20T01:56:36.753-02:00",
    "keyVersion": "1",
    "data": {
            "country":"US",
            "currency": "USD",
            "totalAmount": "19.99",
            "componentList": ["CARD","APPLEPAY"],
            "userId": "1447410849000200"
    },
    "appId": "381ded7c863c439a9e29b4519867965a",
    "version": "1.5",
    "merchantNo": ""
}
```

[/applyDropinSession API](https://docs.payermax.com/api.html?docName=New%20Version&docVer=v1.0&docLang=cn#/paths/aggregate-pay-api-gateway-applyDropinSession/post.html) Example of an interface response:

``` json
{
    "msg": "",
    "code": "APPLY_SUCCESS",
    "data": {
            "sessionKey": "964bffa7c9eb4951bd5ba11a2691e5ed",
            "notSupportedComponent": [],
            "clientKey": "8eef820ecbd443b7a608c2e0863750eb"
    }
}
```
### 3.2 Render the Drop in

1. Introduce the CDN package on the relevant HTML page.

``` html

```

2. Embed a `card` button display area on the merchant page using a `div` tag.

``` html
  
    

```

3. Initialize PayerMax Frames。

``` js
// Initializing card components
const card = PMdropin.create('card', {
  clientKey: "Client Public Key", // obtained in step 1.1 data.clientKey
  sessionKey: "Session Token", // obtained in step 1.1 data.sessionKey
  sandbox: false, // The default is false, which means that the production environment
  hideSaveCard: false, //Whether to hide the save card information option, the default is false show
  hideCardBrands: false, //Whether to hide the logo of the card brand in the upper left corner, the default is false show
});
// Example of mounting
card.mount('.frame-card'); // Mounts to the first dom element it matches.
// Timing of component loading completion
card.on('ready', () => {
    // Remove customized loading              
})
```

4. Listen to form filling status and set payment button dynamically (optional). By listening to the events of the form, you can monitor the legitimacy of the information filled in by the user in real time, so as to dynamically set whether the **Payment** button can be clicked or not.

``` js
card.on('form-check', (res) => {
  // res.isFormValid is the form status. The value is false/true
  // true means the form passes the validation and can be paid, false means the validation doesn't pass and can't be paid, and the pay button can't be clicked.
  console.log('[dropin][form-check]:', res)
})
```

5. The user clicks the payment button, and the merchant submits the payment to the PayerMax server.
```js 
// The user clicks the payment button to submit the payment
function goPay(){
    card.emit('setDisabled', true) // Freeze the form after clicking the payment button to prevent duplicate submissions during the payment process
    card.emit('canMakePayment')
      .then(res => {
        if (res.code === 'APPLY_SUCCESS') {
          const paymentToken = res.data.paymentToken // Payment token, used by the payment API
          // Initiate the payment API
          // The merchant requests their own backend interface to place an order
          // The merchant constructs the request parameters using "params" on their own, and needs to include the paymentToken
            _postapi('orderAndPay',params).then(res =>{
              const code = (res || {}).code
              // The merchant returns the payment result to the frontend
              if (code == 'APPLY_SUCCESS') {  
                  if(res.threeDSUrl){ 
                // Complete 3DS using the pop-up window within the PayerMax component; alternatively, open the threeDSUrl in a new browser tab to complete 3DS
                    handle3DS(threeDSUrl)
                }
                // Payment successful, display the payment result
              }  else {
                  // Payment failed, display the failure result
              }
        }
          card.emit('setDisabled', false) // Unfreeze the form after the payment API is completed
        }
      })
      .catch(err => {
        card.emit('setDisabled', false) // Unfreeze the form if an exception occurs
      })
}

// After orderAndPay, if the returned URL (which is the threeDSUrl) is obtained, use the handle3DS method to trigger the pop-up window
function handle3DS(threeDSUrl) {
  card.create3DSPopup({
    url: threeDSUrl,
    // If width/height are not passed, they will be automatically calculated based on the device and viewport; they can also be explicitly passed as '80%', '400px', etc.
  })
  .then(res => {
    if (res.code === '3DS_PROCESSED') {
      // The 3DS process is completed, continue with the payment flow.
      console.log('3DS process completed', res.data);
      // At this point, the merchant needs to maintain the existing logic, and the merchant side should actively query the payment result to proceed with subsequent merchant-side interactions.
      proceedPayment(res.data);
    }
  })
  .catch(err => {
    if (err.code === 'USER_CANCEL') {
      // The merchant may choose not to handle this, but should allow the user to click the payment button again
      showMessage('You have closed the authentication window');
    } else {
      showMessage('An exception occurred during authentication, please try again');
    }
  });
}
```
### 3.3 Create Payment

1. Merchant client: the user clicks to initiate a payment, checks if the payment is available, and gets the `paymentToken`.

``` js
card.emit('setDisabled', true) // Freeze the form after clicking the payment button to prevent double submission of the payment process
card.emit('canMakePayment')
  .then(res => {
    if (res.code === 'APPLY_SUCCESS') {
      const paymentToken = res.data.paymentToken // Payment token, payment interface usage
      // Initiates the payment interface 
      // The merchant itself requests the backend interface to place the order
      // The merchant itself constructs the request parameters with params, which need to be accompanied by a paymentToken.
        _postapi('orderAndPay',params).then(res =>{
          const code = (res || {}).code
          //Merchants return payment results to the front end
          if (code == 'APPLY_SUCCESS') {     
            //Payment success, show payment result 
          } else {
            //Payment failure, show payment result
          }
    }
      card.emit('setDisabled', false) // Unfreeze form after payment interface completion
    }
  })
  .catch(err => {
    card.emit('setDisabled', false) // Unfreeze form after an exception occurs
  })
```

2. Merchant server: calls the [/orderAndPay API](https://docs.payermax.com/api.html?docName=New%20Version&docVer=v1.0&docLang=cn#/paths/aggregate-pay-api-gateway-orderAndPay(for-drop-dont-copy-me)/post.html) interface to initiate the HTTP POST request and create the payment.

::: warning Note:
1. In card payment, 3DS authentication process may be triggered. Please refer to [Card Payment - Drop In Integration - Complete 3DS Authentication](https://docs.payermax.com/en/202606-version/acquiring/start-integration/integrate-by-payment-method/card/frontend-component.html#_5-4-complete-3ds-authentication).
2. After the payment is created, the merchant can specify the payment shutdown time in seconds for a single payment via the interface entry `expireTime`, the value of which must be greater than 1800 (30 minutes) and less than 86400 (24 hours). If the value passed in is less than 1800, the system will reset to the minimum value of 30min; if the value passed in is less than 86400, the system will reset to the maximum value of 86400. 
If the merchant doesn't specify it, the specific shutdown time will be different according to the payment method used.
:::

[/orderAndPay API](https://docs.payermax.com/api.html?docName=New%20Version&docVer=v1.0&docLang=cn#/paths/aggregate-pay-api-gateway-orderAndPay(for-drop-dont-copy-me)/post.html) Example of an interface request:

``` js
curl --request POST \
  --url 'https://pay-gate-uat.payermax.com/aggregate-pay/api/gateway/orderAndPay' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header 'sign: FPFVT3o227JrFRbqu19boZCpVVTF9KznxyRawUmxpfXilHV/0yK46haPhAjNu1hPUMy7Vw/ILXhfzffNm4Fj0apWknlTY9OJxnSoQxS9BTFtc61tn5yV1q69x/kkBl82/qwg+XTJ4fOzy7Mar3VaC1E2PlDA6RkkKBUyNE6RYgsdB+Su7an4+4HVTNAnoe74WyvBgxTLMNg28igBTdqxaO3w/UBY6ObVp7vkqkQGdL1Y+HgmMYaAVwrM3+ALWGId0sJ+YqTY4WJ+0xCRGhaSnybiIjZsQEYyID68WNUfuavDLDsEhaMm/HfQvf5p0R1Ltovp3wwJnEbQcjY458iX5A==' \
  --data '{
    "requestTime": "2025-05-28T03:52:42.591-02:00",
    "keyVersion": "1",
    "appId": "tested7c863c439a9e29b4519867965a",
    "version": "1.5",
    "merchantNo": "TEST10116880289",
    "data": {
        "integrate": "Direct_Payment", # In Drop In mode, specify Direct_Payment
        "totalAmount": 39.99,
        "country": "SA",
        "expireTime": "3600",
        "paymentDetail": {
            # When payment is made, it is fetched via the canMakePayment event response of the JS SDK's emit interface, non-null
            "paymentToken": "TEST12637c2c2d942239d9a2661c4ad14f9", 
            "buyerInfo": {
                "clientIp": "176.16.34.144",
                "userAgent": "Chrome"
            },
            # When paid, fetched via the response of the JS SDK's create interface, non-null
            "sessionKey": "test29632c3643768e3b65ef6a31c9ce" # Non-null in Drop In mode
        },
        "frontCallbackUrl": "https://front.your.com/pay/index.html",
        "subject": "River Game HK Limited",
        "outTradeNo": "ov1_da78b1f3c2f9443b966347fc89305fc9",
        "notifyUrl": "https://notify.your.com/pay/paymentWebHookPayerMaxServlet",
        "currency": "SAR",
        "userId": "1822613953000446",
        "terminalType": "WEB"
    }
}'
```

[/orderAndPay API](https://docs.payermax.com/api.html?docName=New%20Version&docVer=v1.0&docLang=cn#/paths/aggregate-pay-api-gateway-orderAndPay(for-drop-dont-copy-me)/post.html) Example of an interface response:

``` json
{
    "msg": "Success.",
    "code": "APPLY_SUCCESS",
    "data": {
        "outTradeNo": "test_da78b1f3c2f9443b966347fc89305fc9",
        "tradeToken": "T2024052805951921811176",
        "status": "SUCCESS"
    }
}
``` 

### 3.4 Get Payment Result
#### 3.4.1 Payment Result Notification

See [Get Payment Result Integration - Payment Result Notification](https://docs.payermax.com/en/202606-version/acquiring/start-integration/related-capabilities-integration/payment-result.md#_3-1-payment-result-notification).

#### 3.4.2 Payment Result Inquiry

See [Get Payment Result Integration - Payment Result Inquiry](en/202506-version/acquiring/start-integration/related-capabilities-integration/payment-result.html#_3-2-payment-result-inquiry).

# 4.Card Payment Frontend API

## 4.1 API
Use `PMdropin.API`.
| **API**            | **Description**          | **Details**          |
|---------------------|---------------------|---------------------|
| create | Instantiate a built-in component |  refer to **4.1.1 create**       |
|  mount      | Mount instantiated component to `div` tag     |  refer to 参阅**4.1.2 mount**     |
| on           | Listen events            |  refer to **4.1.3 on**    |
| emit            | Trigger events            |  refer to **4.1.5 emit**       |

#### 4.1.1 create
Used to initialize components, please use `PMdropin.create(ComponentName, Options)`.

**ComponentName Explanation**

| **ComponentName**            | **Field Type**          | **Description**          |
|---------------------|---------------------|---------------------|
| Card | String |  Card Compoment      |

**Options Explanation**

| **Options**            | **Required Or Not**          | **Field Type**          | **Default Value**          |
|---------------------|---------------------|---------------------|---------------------|
| clientKey | Y |  String      |  -      |
| sessionKey | Y |  String      |  -      |
| sandbox | N |  Boolean      |  `false`      |
| language | N |  String      |  `en`      |
| theme | N |  String      |  `light`      |
| customLocalization | N |  Object      |  -      |
| customTheme | N |  Object      |  -      |
| grayscale | N |  String      |  `'0'`      |
| isRtl | N |  Boolean      |  `false`      |
| hideSaveCard | N |  Boolean      |  `false`      |
| hideCardBrands | N |  Boolean      |  `false`      |
| hideCardHolderName | N |  Boolean      |  `false`      |
| saveCardChecked | N |  Boolean      |  `true`      |
| ignoreUserCheckedCache | N |  Boolean      |  `false`      |
| hideRecommendAccount | N |  Boolean      |  `false`      |
| openTermsInCurrentPage | N |  Boolean      |  `false`      |
| termsPopupConfig | N |  Object      |   -     |
| >>showMask | N |  Boolean      |  `true`      |
| >>closeOnClickMask | N |  Boolean      |  `true`      |

#### 4.1.2 mount
Used to mount initialization component instances, please use `PMdropin.mount(Tag)`.

**Tag Explanation**

| **Tag**            | **Description**          |
|---------------------|---------------------|
| id | The value of the id element that needs to be mounted, such as `PMdropin.mount('#card-frame')`|
| class | The value of the class element that needs to be mounted, such as `PMdropin.mount('.card-frame')`|

#### 4.1.3 on
Used to listen to component built-in response events, please use `PMdropin.on(Event, CallbackFunction)`.

**Event Explanation**

| **Tag**            | **Description**          | **Returned Value**          |
|---------------------|---------------------|---------------------|
| ready | Triggered when the component completes loading |`null` |
| form-check | Monitor card component verification status real-time| **refer to Event Response**|
| load | Triggered when the component completes loading | Object 
 - type：Component type card/applepay/googlepay 
- code：Response code SUCCESS indicates that the loading is successful, and the rest of the enumerations indicate that the loading failed 
- msg：response message|

Example：

```js
PMdropin.on('form-check', function(event) {
  if (event.isFormValid) {
    // Form verification passed
  }
});
```

#### 4.1.4 create3DSPopup
Used to open the 3DS authentication page in a pop-up window within the component.

**Language Setting Note:** The SDK automatically retrieves the language from the instance configuration; there is no need to pass it separately in `create3DSPopup`.

**Input Parameter Description:**
| **Parameter** | **Type** | **Required** | **Default Value** | **Description** |
| :--- | :--- | :--- | :--- | :--- |
| url | string | Yes | - | 3DS authentication page URL (obtained from the `orderAndPay` API) |
| showMask | boolean | No | true | Whether to display the background mask |
| width | string | No | Responsive calculation | Width of the pop-up. If not passed, it will be automatically calculated based on the device/viewport. |
| height | string | No | Responsive calculation | Height of the pop-up. If not passed, it will be automatically calculated based on the device/viewport. |

**Returns:**
| **CODE** | **Description** | **Handling Recommendation** |
| :--- | :--- | :--- |
| `3DS_PROCESSED` | 3DS process processed | Continue with the subsequent payment flow, and query the final authentication result via the API. |
| `USER_CANCEL` | User closed the pop-up | Inform the user that it has been closed, allowing them to retry. |
| `INVALID_PARAMS` | Invalid input parameters | Check the input parameters based on `err.msg` (e.g., whether the url is required and non-empty, whether the type is correct, etc.), fix them, and retry. |

#### 4.1.5 emit
Used to call component built-in methods, please use `PMdropin.emit(Event, Params)`.

| **Event**            | **Params**          | **Description**          |
|---------------------|---------------------|---------------------|
| canMakePayment | - |  Get card identification      |
| switchLanguage | string |  Switch language      |
| switchTheme | string |  Switch theme      |
| addLocalization | Object |  Add customized language      |
| addTheme | Object |  Add customized theme      |
| setDisabled | Boolean |  Set component availability status      |
| setRtl | Boolean |  Set component layout from right to left      |
| setGrayscale | string |  Set component page grayscale      |

##### 1.emit.canMakePayment
Check whether the current component status meets the conditions for initiating payment, and return the card identification if the verification passes.

```js
// Example
PMdropin.emit('canMakePayment')
  .then(function(response) {
    // Verification successful
    if (response.code === 'APPLY_SUCCESS') {
      // Get paymentToken 
      var paymentToken = response.data.paymentToken
      // Go for subsequent payment operations
    }
  })
  .catch(function(error) {
    // Catching internal errors
    console.log(error);
  })
```

canMakePayment Response：

```js
// Successful API example
{
  code: 'APPLY_SUCCESS',
  data: {
    // Card indentification
    paymentToken: 'xxx'
  },
  msg: ''
}

// Failed API example
{
  code: 'FORM_INVALID',
  msg: 'Invalid params: cvv'
}
```

| **CODE码**            | **Description**          |
|---------------------|---------------------|
| APPLY_SUCCESS | Successfully obtain `paymentToken` |
| FORM_INVALID | Form validation failed |
| UNKNOWN_ISSUE | Exception information |

##### 2. emit.switchLanguage
You can quickly set localization parameters from our preset language list. For example, `language: 'zh'`.
The supported preset languages are as follows:
| **Language** | **Language Code** |
|---------------------|---------------------|
| English             | en `Default`        |
| Chinese             | zh                  |

+ Initialize with a preset language:

```js
PMdropin.create('card', {
  clientKey: '',
  sessionKey: '',
  language: 'zh',
  ...
});
```

+ Dynamically switch language:

```js
PMdropin.emit('switchLanguage', 'zh')
```

##### 3. emit.addLocalization
When the preset languages cannot meet your localization requirements, you can customize the language fields via `customLocalization`.

+ Initialization Example:

```js
PMdropin.create('card', {
  clientKey: '',
  sessionKey: '',
  customLocalization: {
    // Example for adding Chinese
    'zh': {
      loading: '加载中',
      loadingFailed: '加载失败',
      refresh: '刷新',
      confirm: '确定',
      cancel: '取消',
      removeCard: '移除卡',
      removeCardTip: '确定移除当前选中卡吗?',
      addNewCard: '使用新卡',
      useSavedCard: '使用已存卡',
      cardnum: '银行卡号',
      cardnumHint: 'XXXX XXXX XXXX XXXX',
      cardnumErrTip: '卡号不正确',
      cardbinErrTip: {
        // {cardOrg} is a variable field, do not translate
        CARD_NOT_SUPPORT: '不支持{cardOrg}，请检查卡号或者更换其他卡重试',
        // {cardType} is a variable field, do not translate
        CARD_INVALID: '不支持{cardType}的卡类型',
        CARD_NO_INVALID: '请确认卡号输入是否正确',
      },
      expdate: '过期日期',
      expdateHint: '月/年',
      expdateErrTip: '过期日期不正确',
      cvv: 'CVV/CVC',
      cvvHint: '123',
      cvvErrTip: 'CVV/CVC 格式不正确',
      name: '持卡人姓名',
      nameHint: 'XX XX',
      nameErrTip: '姓名格式不正确',
      saveCardInfoTip: '为下次支付保存信息',
      // The following are newly added multilingual fields
      // Extra collection elements for local cards
      buyerEmail: '邮箱',
      buyerEmailHint: '',
      buyerEmailErrTip: '格式错误，请复核',
      buyerFullName: '姓名',
      buyerFullNameHint: '',
      buyerFullNameErrTip: '格式错误，请复核',
      buyerFirstName: '名',
      buyerFirstNameHint: '',
      buyerFirstNameErrTip: '格式错误，请复核',
      buyerLastName: '姓',
      buyerLastNameHint: '',
      buyerLastNameErrTip: '格式错误，请复核',
      // Peru Document(ID)
      dni: 'DNI',
      dniHint: '',
      dniErrTip: '格式错误，请复核',
      // Argentina Document(ID)
      dni_cuit: 'DNI/CUIT',
      dni_cuitHint: '',
      dni_cuitErrTip: '格式错误，请复核',
      // South Africa Document(ID)
      idCard: 'ID',
      idCardHint: '',
      idCardErrTip: '格式错误，请复核',
      // Colombia Document(ID)
      cc: 'CC',
      ccHint: '',
      ccErrTip: '格式错误，请复核',
      // Mexico Document(ID)
      curp: 'CURP',
      curpHint: '',
      curpErrTip: '格式错误，请复核',
      // Brazil Document(ID)
      cpf: 'CPF',
      cpfHint: '',
      cpfErrTip: '格式错误，请复核',
      // Chile/Paraguay/Uruguay Document(ID)
      ci: 'CI',
      ciHint: '',
      ciErrTip: '格式错误，请复核',
      buyerPhoneNo: '手机号码',
      buyerPhoneNoHint: '',
      buyerPhoneNoErrTip: '格式错误，请复核',
      buyerPhoneNoRegion: '手机区号',
      buyerPhoneNoRegionHint: '',
      buyerPhoneNoRegionErrTip: '格式错误，请复核',
    }
  },
  ...
});
```
+ Dynamically add language:

```js
PMdropin.emit('addLocalization', {
  'zh': {
    // Parameters as shown above
  }
});
```

##### 4. emit.switchTheme
Use our theme functionality to help build a style combination that better matches your own website.

+ Use Preset Themes

We provide two preset theme colors. You can customize the theme using the `theme` parameter.
The supported preset themes are as follows:

| **Theme Name** | **Theme Code** |
|---------------------|---------------------|
| White               | light `Default`     |
| Black               | dark                |

+ Initialization Example:

```js
PMdropin.create('card', {
  clientKey: '',
  sessionKey: '',
  theme: 'dark'
});
```

+ Or dynamically switch theme:

```js
PMdropin.emit('switchTheme', 'dark');
```

##### 5. emit.addTheme
For custom themes, you can modify styles by passing a custom theme package. The example is as follows:

```js
PMdropin.create('card', {
  clientKey: '',
  sessionKey: '',
  theme: 'red',
  customTheme: [
    {
      // Theme name [Required]
      name: 'red', 
      // Style used to fill the background [Optional]
      base: 'light',
      style: `:root {
          /* --- common --- */
          --bg-primary: #ffffff;
          --color-primary: #3782ff;
          --border-color-primary: #eaeaea;
          --border-color-hover-primary: #dcdcdc;
          --font-size-primary: 13px;
          --border-radius-primary: 6px;
        
          /* --- frame --- */
          --padding-frame: 16px;
          --bg-color-frame: var(--bg-primary);
          --bg-color-mask: rgba(255, 255, 255, 0.8);
        
          /* --- text --- */
          --color-text-primary: #333333;
          --color-text-secondary: #666666;
          --color-text-tip: #c8c8c8;
          --color-text-error: #e64949;
        
          /* --- button --- */
          /* plain button */
          --bg-color-btn-plain: var(--bg-primary);
          --border-color-btn-plain: var(--border-color-primary);
          --border-color-hover-btn-plain: var(--border-color-hover-primary);
          --font-size-btn-plain: var(--font-size-primary);
          --border-radius-btn-plain: var(--border-radius-primary);
        
          /* text button */
          --color-btn: var(--color-primary);
          --color-active-btn: #2c68cc;
          --font-size-btn-text: var(--font-size-primary);
        
          /* --- input --- */
          --bg-color-input: var(--bg-primary);
          --border-color-input: var(--border-color-primary);
          --border-color-hover-input: var(--border-color-hover-primary);
          --border-color-focus-input: var(--color-primary);
          --shadow-color-focus-input: rgba(55, 130, 255, 0.2);
          --label-color-input: var(--color-text-primary);
          --label-color-focus-input: var(--color-primary);
          --action-color-input: var(--border-color-input);
          --action-color-hover-input: var(--border-color-hover-input);
          --action-color-active-input: #bbbbbb;
          --color-placeholder-input: #dcdcdc;
          --color-input: var(--color-text-primary);
          --font-size-input: 12px;
          --font-size-input-label: var(--font-size-input);
          --font-size-input-tip: var(--font-size-input);
          --height-input: 36px;
          --size-checkbox: 16px;
          --border-radius-input: var(--border-radius-primary);
          --border-radius-checkbox: calc(var(--border-radius-input)/2);
        
          /* --- item --- */
          --bg-color-item: var(--bg-primary);
          --bg-color-selected-item: #f5f9ff;
          --border-color-item: var(--border-color-primary);
          --border-color-hover-item: var(--border-color-hover-primary);
          --border-color-selected-item: var(--color-primary);
          --font-size-item: 15px;
          --height-item: 36px;
          --height-item-btn: var(--height-item);
          --height-selected-item: 48px;
          --border-radius-item: var(--border-radius-primary);
        
          /* --- others --- */
          --divider-color: #f4f4f4;
        }`
    }
  ]
});
```

+ Or dynamically add theme:

```js
PMdropin.emit('addTheme', [
  {
    name: 'red', 
    base: 'light',
    style: ''
  }
]);
```

##### 6. emit.setDisabled
+ Set component availability status
+ Type: `Boolean`
+ Default: `false`

```js
// Uneditable status
PMdropin.emit('setDisabled', true)

// Editable status
PMdropin.emit('setDisabled', false)
```

##### 7. emit.setRtl
+ Set component layout from right to left
+ Type: `Boolean`
+ Default: `false`

```js
// Right to left Layout
PMdropin.emit('setRtl', true)

// Left to right layout
PMdropin.emit('setRtl', false)
```

##### 8. emit.setGrayscale
+ Set component page grayscale
+ Type: `String`
+ Default: `0`

```js
// Set 0 - 1 grayscale value
PMdropin.emit('setGrayscale', '1')
```
