﻿# Drop in - GooglePay

## 1. API Introduction

### 1.1 API List

| Sequence of Interaction                               | Direction                | API Type      | API PATH                                                                                                                                                                      |
| ----------------------------------------------------- | ------------------------ | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 2.1 Obtain Initialization Info for Frontend Component | `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)          |
| 2.3 Create Payment, Call Frontend Component Order 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) |
| 2.4.1 Asynchronous Payment Result Notification        | `PayerMax` -> `Merchant` | `Backend API` | [/collectResultNotifyUrl](https://docs.payermax.com/api.html?docName=New%20Version&docVer=v1.0&docLang=cn#/paths/collectResultNotifyUrl/post.html)                            |
| 2.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)                          |

### 1.2 Environment Information

- **Sandbox Environment**: `https://pay-gate-uat.payermax.com/aggregate-pay/api/gateway/<API PATH>`

- **Production Environment**: `https://pay-gate.payermax.com/aggregate-pay/api/gateway/<API PATH>`

### 1.3 Request Headers

``` json
"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"
}
```

## 2. Start Integration
### 2.1 Obtain Initialization Info for Frontend Component

The merchant server initiates an HTTP POST request 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) to obtain the client token `clientKey` and session token `sessionKey` required for frontend component initialization.

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

``` js
{
    "requestTime": "2024-11-20T01:56:36.753-02:00",
    "keyVersion": "1",
    "data": {
            "country":"US",
            "currency": "USD",
            "totalAmount": "19.99",
            "componentList": ["CARD","GOOGLEPAY"],
            "userId": "1447410849000200"
    },
    "appId": "381ded7c863c439a9e29b4519867965a",
    "version": "1.5",
    "merchantNo": ""
}
```
+ Request Response Example for [/applyDropinSession API](https://docs.payermax.com/api.html?docName=New%20Version&docVer=v1.0&docLang=cn#/paths/aggregate-pay-api-gateway-applyDropinSession/post.html):

``` json
{
    "msg": "",
    "code": "APPLY_SUCCESS",
    "data": {
            "sessionKey": "964bffa7c9eb4951bd5ba11a2691e5ed",
            "notSupportedComponent": [],
            "clientKey": "8eef820ecbd443b7a608c2e0863750eb"
    }
}
```

### 2.2 Render Frontend Component

1. Include the CDN package in the relevant HTML page.

``` html

```

2. Embed a container area for the `ApplePay` button on the merchant page using a `div` tag.

``` html
  
    

```

3. Initialize PayerMax Frames and mount GooglePay.

``` js
const googlePay = PMdropin.create('googlepay', {
    clientKey: "8eef820ecbd443b7a608c2e0863750eb",
    sessionKey: "0fdde7eda5fe4cc28ca8fdc759e28dc1",
    sandbox: true,
    payButtonConfig: {   
        buttonRadius: "12",
        buttonColor: "white",
        buttonType: "order",
        buttonLocale: "en",
        width: "240px",
        height: "40px"
    }
});
//Mount the button
googlePay.mount('.frame-googlepay');
```

4. Listen to the button status. Once the user completes the Google Pay authentication flow, the frontend receives the `paymentToken`. The server then initiates the payment and returns the `redirectUrl` to the frontend to guide the user through 3DS authentication.

``` SQL
googlePay.on('ready', () => {
        console.log('Component initialized');
    })
googlePay.on('load', (res = {}) => {
    const { code, msg } = res || {};
    if (code === "SUCCESS") {
        console.log('[merchant][load]success:', res)
    } else {
        console.log('[merchant][load]fail:', res)
    }
})
googlePay.on('payButtonClick', (res) => {
    googlePay.emit('setDisabled', true);
    googlePay.emit('canMakePayment')
        .then(paymentRes => {
            console.log('canMakePayment')
            if (paymentRes.code === 'APPLY_SUCCESS') {
                const paymentToken = paymentRes?.data?.paymentToken;
                console.log('paymentToken:', paymentToken)
                // ⚠️ Initiate payment API
                // The merchant requests their own backend API to place the order.
                // The merchant constructs the request parameters using params, which must include the paymentToken
                const params = {  // Add params definition
                    token: paymentToken,
                };
                _postapi('orderAndPay', params)
                    .then(apiRes => {
                        const code = (apiRes || {}).code;
                        if (code === 'APPLY_SUCCESS') { 
                            if(apiRes.threeDSUrl){
                            handle3DS(threeDSUrl)
                            // Complete 3DS via the PayerMax in-component popup, or open threeDSUrl in a new browser tab to complete 3DS
                            }    
                            googlePay.emit('paySuccess');// Payment successful, the Google Pay component disappears
                        } else {
                            googlePay.emit('payFail');// Payment failed, the Google Pay component disappears
                        }
                        googlePay.emit('setDisabled', false);// ⚠️ Unfreeze the form after the payment API call completes
                    })
                    .catch(err => {
                        console.error('API request failed:', err);
                        googlePay.emit('setDisabled', false);// Unfreeze the form if an exception occurs
                    });
            }
        })
        .catch(err => {
            console.error('Payment capability check failed:', err);
            googlePay.emit('setDisabled', false);
        });
}); 

// If the returned URL (which is the 'threeDSUrl') is obtained after 'orderAndPay', use the 'handle3DS' method to invoke the popup
function handle3DS(threeDSUrl) {
  googlePay.create3DSPopup({
    url: threeDSUrl,
    // If width/height is not passed, it will be automatically calculated based on the device and viewport; it can also be explicitly passed as '80%', '400px', etc
  })
  .then(res => {
    if (res.code === '3DS_PROCESSED') {
      // 3DS flow completed, proceed with the payment process.
      console.log('3DS flow completed', res.data);
      // The merchant needs to maintain the existing logic here; the merchant side should actively query the payment result to continue subsequent interactions
      proceedPayment(res.data);
    }
  })
  .catch(err => {
    if (err.code === 'USER_CANCEL') {
      // The merchant can choose not to handle this, but should allow the user to continue clicking the payment button
      showMessage('You have closed the authentication window');
    } else {
      showMessage('An exception occurred during authentication, please try again');
    }
  });
}
```
5. Get the `paymentToken`. Once the user confirms the payment on the frontend, the frontend `canMakePayment` API will return the following information, which is used for the backend to initiate the payment.

```Json
{
    "msg": "",
    "code": "APPLY_SUCCESS",
    "data": {
        "paymentToken": "CPT771e98494eff41f1a03a715ebab69cc9",
        "cardOrg": "VISA",
        "maskCardNumber": "444433****1111",
        "cardExpirationMonth": "12",
        "cardType": "CREDIT",
        "cardExpirationYear": "26",
        "cardHolderFullName": "Jemy Cheung",
        "agreementAccepted": true,
        "cardBinNo": "444433",
        "cardIssuingCountry": "US"
    }
}
```

### 2.3 Create Payment

1. Merchant Server: Call 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) to initiate an HTTP POST request and create a payment.

+ [/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) request example:

```diff js
{
        "requestTime": "2024-11-20T01:56:45.802-02:00",
        "keyVersion": "1.5", 
        "data": {
        "currency": "USD",
        "country":"US",
        "totalAmount": 19.99,      
        "expireTime": "3600",
        "paymentDetail": {
+               "paymentToken": "dbe78b7dd4fa4b668bacfdcc7153821d",//Callback to frontend paymentDetail after the user enters card info and clicks pay
                "buyerInfo": {
                        "clientIp": "146.75.136.237",
                        "userAgent": "Chrome"
                },
+               "sessionKey": "abf62fdcf9c4408cb73117db6e740713"
        },
        "frontCallbackUrl": "https://",
        "subject": "xxx Game HK Limited",
        "outTradeNo": "ov1_5b89ced71d764ed9994e6882d88082f7",
        "notifyUrl": "https://",
        "userId": "1447410849000200",
        "integrate": "Direct_Payment",
        "terminalType": "WEB"
        },
        "appId": "381ded7c863c439a9e29b4519867965a",
        "version": "1.5",
        "merchantNo": "P04010116880289"
}
```

+ [/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.htlm) response example:

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

### 2.4 Get Payment Result

The `data.status` in the response of the [Create Payment/orderAndPay API](https://docs.payermax.com/api.html?docName=New%20Version&docVer=v1.0&docLang=cn#/paths/aggregate-pay-api-gateway-orderAndPay/post.html) is not the final payment status. Therefore, merchants should not use it directly to update the payment result.

#### 2.4.1 Payment Result Notification

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

#### 2.4.2 Payment Result Inquiry

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

## 3. API

Usage method: `PMdropin.API`.

| **API**            | **Description**          | **Details**          |
|---------------------|---------------------|---------------------|
| create | Instantiate a built-in component |  Refer**3.1 create**     |
|  mount      | Mount instantiated component to `div` tag      |  Refer**3.2 mount**      |
| on           | Listen events            |  Refer**3.3 on**     |
| emit            | Trigger events            |  Refer**3.5 emit**     |

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

**ComponentName Explanation**

| **ComponentName**            | **Field Type**          | **Description**          |
|---------------------|---------------------|---------------------|
| applepay | string |  ApplePay Compoment      |

**Options Explanation**

| **Options**            | **Required Or Not**          | **Field Type**          | **Default Value**          | 
|---------------------|---------------------|---------------------|---------------------|
| clientKey | Y |  String      |  -      |
| sessionKey | Y |  String      |  -      |
| sandbox | N |  Boolean      |  `false`      |
| theme | N |  String      | `light`      |
| payButtonStyle | N |  String      |  -      |

### 3.2 mount
sed 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('#applepay-frame')`|
| class | The value of the class element that needs to be mounted, such as `PMdropin.mount('.applepay-frame')`|

### 3.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` |
| payButtonClick | Triggered when the ApplePay button is clicked| `null`|

### 3.4 create3DSPopup

Used to open the 3DS authentication page within an in-component popup.

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

**Input Parameters:**
| **Parameter** | **Type** | **Required** | **Default Value** | **Description** |
| :--- | :--- | :--- | :--- | :--- |
| url | string | Y | - | The 3DS authentication page URL (obtained from the `orderAndPay` API). |
| showMask | boolean | N | true | Whether to display the background mask overlay. |
| width | string | N | Responsive | Popup width. If omitted, it will be automatically calculated based on the device/viewport. |
| height | string | N | Responsive | Popup height. If omitted, it will be automatically calculated based on the device/viewport. |

**Returns:**
| **CODE** | **Description** | **Handling Suggestions** |
| :--- | :--- | :--- |
| `3DS_PROCESSED` | 3DS process has been handled. | Proceed with the subsequent payment flow, and actively query the final authentication result via the API. |
| `USER_CANCEL` | User closed the popup. | Prompt the user that the window has been closed, and allow them to retry. |
| `INVALID_PARAMS` | Invalid input parameters for the call. | Check the input parameters based on `err.msg` (e.g., ensure the URL is required, non-empty, and of the correct type), then retry after correction. |

### 3.5 emit
用于调用组件内置方法，使用方法`PMdropin.emit(Event, Params)`。

| **Event**            | **Params**          | **描述**          |
|---------------------|---------------------|---------------------|
| canMakePayment | Object |  获取本次支付token      |
| switchTheme | string |  切换主题      |
| setDisabled | Boolean |  设置组件可用状态      |
| setpayButtonConfig | Object |  设置按钮样式      |

#### 3.5.1 emit.canMakePayment

1. Checks whether the current component status meets the conditions to initiate a payment, and returns the `paymentToken` after verification passes.

```js
PMdropin.emit('canMakePayment', params?)
```
`params` is an optional parameter. If the merchant server has already provided complete payment information when calling the `/applyDropinSession` API, you can call `PMdropin.emit('canMakePayment')` directly without passing parameters.

2. Options Configuration:
| **Options** | **Required** | **Field Type** | **Description** |
| :--- | :---: | :---: | :--- |
| totalAmount | N | String | Payment amount, formatted as a pure numeric string (e.g., '1.00'). This is only recommended for scenarios where the merchant server did not pass the amount when calling the `/applyDropinSession` API to obtain pre-component initialization information. This amount is solely used for display within the Apple Pay payment sheet. |

**Example:**

```js
PMdropin.emit('canMakePayment', {
  totalAmount: '1.00'
})
```
**Notes:**
> - `totalAmount` only accepts valid numeric strings (e.g., `'0.01'`, `'99.99'`). If an invalid value is passed, the API will return the error code `AMOUNT_INVALID`.
> - If the merchant server has already provided the amount when calling `/applyDropinSession`, the amount used during final order submission must be consistent with it; otherwise, it may lead to payment failure or risk control interception.
> - Please ensure that the `totalAmount` passed in `canMakePayment` is strictly consistent with the amount during final order submission; otherwise, it may lead to payment failure or risk control interception.

canMakePayment Response:

| **Code** | **Description** |
|---------------------|---------------------|
| APPLY_SUCCESS | Successfully obtained the `paymentToken`. |
| UNKNOWN_ISSUE | Exception occurred / Internal error. |
| AMOUNT_INVALID | Invalid input amount format. |

#### 3.5.2 emit.setDisabled
+ Set the component available state
+ Type: `Boolean`
+ Default: `false`

```js
// Button disabled state
PMdropin.emit('setDisabled', true)

// Button enabled state
PMdropin.emit('setDisabled', false)
```

#### 3.5.3 emit.setpayButtonConfig

+ Sets the button styles
+ Type: `Object`
+ Default:

```Go
{
  buttonRadius: "12", // Sets the Google Pay button border radius. String
  buttonColor: 'default', // Sets the button color. "default"/"white"/"black"
  buttonType: 'plain',
  buttonLocale: 'en',
  width: "240px",
  height: "40px",
}
```

## 4. Internal Field Description

| Field        | Default Value | Type   | Enumeration                                                        | Description                                                                                                                                                         |
| ------------ | ------------- | ------ | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| width        | "240px"       | String | /                                                                  | Set Google Pay button width                                                                                                                                         |
| height       | "40px"        | String | /                                                                  | Set Google Pay button height                                                                                                                                        |
| buttonRadius | "12"          | String | /                                                                  | Set the border radius of the googlepay button String                                                                                                                |
| buttonColor  | 'default'     | String | 'default'/'white'/ 'black'                                         | Set button color                                                                                                                                                    |
| buttonType   | 'plain'       | String | "plain","buy","book","checkout","donate","order","pay","subscribe" | Set button type Same as GooglePay official. See [Preview demo](https://developers.google.com/pay/api/web/guides/resources/customize?hl=zh-cn) for customized button |
| buttonLocale | 'en'          | String | 'en'/'ja'/'zh'/... International language encoding                 | Set the multi-language text on the button, the same as GooglePay official, only supports the specified type, such as "checkout", "donate", etc.                     |

## 5. Color Display

| Input   | Effect Preview                                                                                |
| ------- | --------------------------------------------------------------------------------------------- |
| default | ![](https://img-cdn-sg.payermax.com/public/20250305-73376124-4e97-46de-88d1-898893b40e57.png) |
| white   | ![](https://img-cdn-sg.payermax.com/public/20250305-e3b076cd-8074-4d84-b24f-2845f80956de.png) |
| black   | ![](https://img-cdn-sg.payermax.com/public/20250305-8be017d4-807d-48a3-9659-705e2e6de1cb.png) |
| 入参    | 效果预览                                                                                      |
| ------- | --------------------------------------------------------------------------------------------- |
| default | ![](https://img-cdn-sg.payermax.com/public/20250305-73376124-4e97-46de-88d1-898893b40e57.png) |
| white   | ![](https://img-cdn-sg.payermax.com/public/20250305-e3b076cd-8074-4d84-b24f-2845f80956de.png) |
| black   | ![](https://img-cdn-sg.payermax.com/public/20250305-8be017d4-807d-48a3-9659-705e2e6de1cb.png) |

## 6. buttonType Display

| Input       | Effect Preview                                                                                |
| ----------- | --------------------------------------------------------------------------------------------- |
| "plain"     | ![](https://img-cdn-sg.payermax.com/public/20250306-df875c3c-83e8-40e8-9858-843a88025f45.png) |
| "buy"       | ![](https://img-cdn-sg.payermax.com/public/20250306-cf849d09-3d49-46ea-8b50-e5b82804708e.png) |
| "book"      | ![](https://img-cdn-sg.payermax.com/public/20250306-a4a2794c-1913-4a46-9f1c-bbee6b9f9cc5.png) |
| "checkout"  | ![](https://img-cdn-sg.payermax.com/public/20250306-e3579e48-c4e4-46ac-adce-bc6f5d9761b4.png) |
| "donate"    | ![](https://img-cdn-sg.payermax.com/public/20250306-0cca324d-be97-4e20-97ed-77caa0ea7930.png) |
| "order"     | ![](https://img-cdn-sg.payermax.com/public/20250306-abe3aa72-c82b-45c9-8631-f76d86137049.png) |
| "pay"       | ![](https://img-cdn-sg.payermax.com/public/20250306-802acd43-41b4-4025-ae8c-2be392ea16e7.png) |
| "subscribe" | ![](https://img-cdn-sg.payermax.com/public/20250306-bf0b5d4e-c94c-4aec-82ae-54103ee3c313.png) |

## 7. buttonLocale Display

| Input | Effect Preview                                                                                                                                                                                      | Description |
| ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
| "ja"  | ![](https://img-cdn-sg.payermax.com/public/20250306-b8ec4b61-0414-441c-87ac-a558a4d6792b.png)         ![](https://img-cdn-sg.payermax.com/public/20250306-6ea1767d-e71d-4e9a-902b-a7a572cd4f04.png) | Japanese    |
| "bg"  | ![](https://img-cdn-sg.payermax.com/public/20250306-200a1309-839d-4d0c-88fa-be9e13532ee3.png)         ![](https://img-cdn-sg.payermax.com/public/20250306-0d463fe2-9765-43b0-a55b-7366b27fa3c1.png) | Bulgarian   |
