﻿# Drop in-ApplePay

## 1. Preparations
::: tip  
ApplePay certificates are uniformly maintained by PayerMax, merchants only need to download the ApplePay certification file and save it in the specified location.
:::
1. Contact PayerMax technical support to provide the domain names of your official and test environments to complete Apple's domain name certification requirements. Especially note that `127.0.0.1`, `LAN IP`, `localhost` can't pull up ApplePay in the test environment.

2. Download the ApplePay authentication file and place it in the specified path `https://[Pending Domain Name Registration]/.well-known/apple-developer-merchantid-domain-association`.

  - Test environment certification documentation: [apple-developer-merchantid-domain-association](https://img-cdn-sg.payermax.com/public/20250703-93d02b05-fc2a-4569-b8a0-65887e5453a6apple-developer-merchantid-domain-association)；

  - Documentation of certification of the production environment: [apple-developer-merchantid-domain-association](https://img-cdn-sg.payermax.com/public/20250730-0a4ca772-d0fa-49c3-82d1-ced08252b290apple-developer-merchantid-domain-association).

3. After the merchant has configured the certificate, notify PayerMax to validate the domain certificate and PayerMax will operate the domain certificate validation in the Apple developer backend. If the validation fails, the possible reasons are as follows:

  - The certificate path is misconfigured;

  - Your firewall is blocking the Apple Check service, please configure the [link page](https://developer.apple.com/documentation/applepayontheweb/setting-up-your-server) domain to the server whitelist;

  - You may have already configured a certificate for another MerchantID. In this case, you can use the new domain name, or delete the corresponding domain certificate on the original developer account.

## 2. API Introduction

### 2.1 Interface List

| Associated Interaction Sequence | Call Direction | API Type | API PATH |
| :--- | :--- | :--- | :--- |
| 3.1 Get initialization information for Drop-in 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) |
| 3.4 Create payment, call Drop-in 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) |
| 3.4.1 Payment result asynchronous notification | PayerMax -> Merchant | Backend API | [/collectResultNotifyUrl](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) |

### 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
{
        "version": "1.5",
        "keyVersion": "1",
        "requestTime": "2025-05-14T16:30:27.174+08:00",
        "appId": "test516e8ab74578be8eecd8c4803fbe",
        "merchantNo": "TEST010117960578",
        "data": {
            "country": "MY", # Acquiring country
            "currency": "MYR", # Order currency
            "totalAmount":"50", # Order amount
            "userId": "20220622_00086", # User ID, must be unique
            "componentList":["APPLEPAY","CARD"] # Specifies the payment methods available for this order
        }
}
```
The actual available payment method types for this order can be specified via the request parameter `componentList`. Currently, only `CARD`, `APPLEPAY`, and `GOOGLEPAY` are supported.
[/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": "success",
  "code": "APPLY_SUCCESS", 
  "data": {
    "sessionKey": "bf2c47b085e24c299e45dd56fd751a70",
    "clientKey": "bbd8d2639a7c4dfd8df7d005294390df" 
    }
}
```
### 3.2 Render the Drop in

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

``` html

```

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

``` html

```

3. Initialize PayerMax Frames。
> Whether the server-side `orderAndPay` API response is a success or a failure, it must be communicated to the frontend. The Apple Pay pop-up window will only disappear after the frontend calls `applepay.emit('paySuccess'/'payFail')`.
``` js
var applepay = PMdropin.create('applepay', {
      // Fill in the clientKey obtained from your server side
      clientKey: clientKey,
      // Fill in the sessionKey obtained from your server side
      sessionKey: sessionKey,
      theme: 'light',
      // Enable sandbox mode for joint debugging
      sandbox: true, //true
    });
    
// Mount the component to the DOM node
applepay.mount('#applepay');
applepay.on('ready', (res) => console.log('[merchant][ready]:', res))
    
applepay.on('payButtonClick', (res) => {
   // Set the component to an uneditable state
  applepay.emit('setDisabled', true);
  // Initiate component validation and return the payment token
  applepay.emit('canMakePayment')
    .then(function(response) {
      // Relieve the uneditable state
      applepay.emit('setDisabled', false);
    
      // Response successfully obtained
      if (response.code === 'APPLY_SUCCESS') {
        // Obtain the payment token (paymentToken)
        var paymentToken = response.data.paymentToken;
        console.log("paymentToken:"+paymentToken);
        // Proceed with subsequent payment operations. The merchant requests their own server side and constructs the request parameters using "params" on their own.
        // The merchant server side calls the payment API (orderAndPay) to initiate the payment.
        if(paymentToken){
              _postapi('orderAndPay',params).then(res =>{
                const code = (res || {}).code
                if (code == 'APPLY_SUCCESS') {    
+                 // After the server side receives the payment success callback, it needs to return the result to the frontend. The frontend calls
+                 // applepay.emit('paySuccess') to make the Apple Pay pop-up disappear and display a payment success prompt.
                  applepay.emit('paySuccess')
                } else {
+                 applepay.emit('payFail') // The pop-up window will disappear
                }  
        })}
      }
    })
    .catch(function(error) {
      // Relieve the uneditable state
      applepay.emit('setDisabled', false);
    })
})
```

4. **Obtaining the paymentToken:** After the user confirms the payment on the frontend, the frontend `canMakePayment` API will receive the following information, which is used for the backend to initiate the payment.

```
``` js
{
    "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"
    }
}
```

### 3.3 Create Payment

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.

[/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:

``` diff js
{
    "requestTime": "2025-05-28T03:52:42.591-02:00",
    "keyVersion": "1",
    "appId": "tested7c863c439a9e29b4519867965a",
    "version": "1.5",
    "merchantNo": "TEST10116880289",
    "data": {
        "integrate": "Direct_Payment", # Specifies Direct_Payment under the hosted component mode
        "totalAmount": 39.99,
        "country": "SA",
        "expireTime": "3600",
        "paymentDetail": {
            # Obtained from the response of the canMakePayment event via the JS SDK emit interface during payment, non-empty
+           "paymentToken": "TEST12637c2c2d942239d9a2661c4ad14f9", 
            "buyerInfo": {
                "clientIp": "176.16.34.144",
                "userAgent": "Chrome"
            },
            # Obtained from the response of the JS SDK create interface during payment, non-empty
+           "sessionKey": "test29632c3643768e3b65ef6a31c9ce" # Non-empty under the hosted component mode
        },
        "frontCallbackUrl": "[https://front.your.com/pay/index.html](https://front.your.com/pay/index.html)",
        "subject": "xx Game",
        "outTradeNo": "ov1_da78b1f3c2f9443b966347fc89305fc9",
        "notifyUrl": "[https://notify.your.com/pay/paymentWebHookPayerMaxServlet](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 Results

[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) The `data.status` of the interface response is not a payment end-state and therefore should not be used by merchants to update payment results directly.

#### 3.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).

#### 3.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).

## 4. ApplePay Frontend API

Use `PMdropin.API`.

| **API**            | **Description**          | **Details**          |
|---------------------|---------------------|---------------------|
| create | Instantiate a built-in component |  Refer **4.1 create**      |
|  mount      | Mount instantiated component to `div` tag      |  Refer **4.2 mount**      |
| on           | Listen events            |  Refer **4.3 on**     |
| emit            | Trigger events            |  Refer **4.4 emit**      |

### 2.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      |  -      |

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

### 2.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`|

Example:

```js
PMdropin.on('payButtonClick', function(event) {
  // Obtain paymentToken and call orderAndPay
  applepay.emit('setDisabled', true)
  applepay.emit('canMakePayment')
    .then(res => {
      const paymentToken = res?.data?.paymentToken 
      if(paymentToken){
       _postapi('orderAndPay',params).then(res =>{
                    const code = (res || {}).code
                    if (code == 'APPLY_SUCCESS') {    
                      // After the server side receives the payment success callback, it needs to return the result to the frontend. The frontend calls
                      // applepay.emit('paySuccess') to make the Apple Pay pop-up disappear and display a payment success prompt.
                      applepay.emit('paySuccess')
                    } else {
                      applepay.emit('payFail')
                    }
      })}else{ 
        applepay.emit('setDisabled', false)
      }
    })
    .catch(err => {
      applepay.emit('payFail')
      applepay.emit('setDisabled', false) 
    })
    
});
```

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

| **Event**            | **Params**          | **Description**          |
|---------------------|---------------------|---------------------|
| canMakePayment | Object |  Get the payment token      |
| switchTheme | string |  Switch theme      |
| setDisabled | Boolean |  Set the component available state      |
| setpayButtonStyle | string |  Set the button style      |

#### 2.4.1 emit.canMakePayment

Checks whether the current component status meets the conditions to initiate a payment, and returns the `paymentToken` after successful validation
```js
PMdropin.emit('canMakePayment', params?)
```
`params` is an optional parameter. If the merchant server side has already provided complete payment information when calling the `/applyDropinSession` API, they can directly call `PMdropin.emit('canMakePayment')` without passing any arguments.

Options Configuration:

| **Options** | **Required** | **Field Type** | **Description** |
| :--- | :---: | :---: | :--- |
| totalAmount | No | String | Payment amount, formatted as a pure numeric string (e.g., '1.00'). It is only recommended for scenarios where the **amount was not provided** when the merchant server side obtained the hosted component initialization information via the `/applyDropinSession` API. This amount is used solely for display within the Apple Pay payment pop-up window. |

**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 side already provided the amount when calling `/applyDropinSession`, the amount used when finally submitting the order must match it consistently; 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 used when finally submitting the order; otherwise, it may lead to payment failure or risk control interception.

canMakePayment Response：

| **Code**            | **Description**          | 
|---------------------|---------------------|
| APPLY_SUCCESS | Get `paymentToken` successfully |  
| UNKNOWN_ISSUE | Exception information | 
| AMOUNT_INVALID | The input amount format is incorrect | 
| APPLEPAY_INTERNAL_ERROR | Apple Pay internal error | 

#### 4.4.2 emit.switchTheme
+ Set the button theme
+ Type: `Boolean`
+ Default: `light`

| **Theme Name**            | **Theme type**          | **Effect Preview**          |
|---------------------|---------------------|---------------------|
| White | light `Default` |  ![](https://img-cdn-sg.payermax.com/public/20240801-ac3e16a0-6aca-4333-8f2b-431df925b8ee.png)      |
| Black | dark |  ![](https://img-cdn-sg.payermax.com/public/20240801-91bfb336-b33d-484d-a142-2ec5a3d8007d.png)      |

#### 2.4.3 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)
```

#### 1.4.4 emit.setpayButtonStyle
+ Set the button style
+ Type: `Boolean`
+ Default: `false`

```js
// Set the width, height, padding, and border radius of the ApplePay button
PMdropin.emit('payButtonStyle', `width:20rem;height:4rem;border-radius: 4rem;padding:1rem 4rem;`)
```
