diff --git a/dips/dip-158.md b/dips/dip-158.md new file mode 100644 index 00000000..98f0b9af --- /dev/null +++ b/dips/dip-158.md @@ -0,0 +1,797 @@ +--- +dip: 158 +title: P2M (eCommerce) Payments Off-Chain Protocol +authors: Yinon Zohar, Daniel Prinz +co-authors: David Wolinsky, Dimitri Roche +status: Draft +type: Standard +created: 03/15/2021 +--- + +# Summary +This DIP leverages the off-chain protocol defined in [DIP-1](https://github.com/diem/dip/blob/main/dips/dip-1.mdx) and extends it to enable eCommerce P2M payments (Peer To Merchant) of the following types: +* Charge +* Auth / Capture + +This DIP defines the type of eCommerce P2M payments, corresponding requests and the payment structures. + +This DIP makes the Auth/Capture section in [DIP-8](https://dip.diem.com/dip-8/#authcapture) redundant. The auth/capture section in DIP-8 is to be disregarded and will be removed in a future version of DIP-8. + +This DIP does NOT include payment flows which require funds pull pre-approval such as subscription and recurring payments. + +This DIP makes use of the `result` field added to the `CommandResponseObject` as described in [DIP-161](https://github.com/diem/dip/blob/main/dips/dip-161.md) + +--- +# Abstract / Motivation +This DIP is an extension of the Off-Chain Protocol to support more advanced payment functionality - particularly eCommerce P2M payments. +## How P2M Payments Differ from P2P Transfers +There are several key differences between the process of paying a merchant (P2M) and a simple P2P transfer: +||P2M|P2P| +|-|-|-| +|**Payer Data**|Customer details are required for **every** payment / purchase in order to perform risk checks and apply AML policies|Required only for transfers for which Travel Rule applies. i.e., over 1000 USD| +|**Payment Owner**|The merchant, i.e. the receiver of funds, "drives" the process and controls the type of payment and its timing. For example, the merchant will decide when to capture the previously authorized funds and whether to capture the entire payment amount or only partial amount |The payer, i.e. the sender of funds, determines the nature of the transfer| +|**Reconciliation**|The merchant requires a way to correlate the transfer of funds to a payment or payment request for reconciliation purposes|No reconciliation| + +## P2M Payment Types +There are two types of P2M payments described in this document: +1. **Charge**. Charge payment stipulates an immediate capture of funds. This means that a successful off-chain negotiation for such payment will result in funds being captured +2. **Auth / Capture**. Auth / Capture payment, as the name suggests, means that the payment is divided to two steps: Authorization and Capture. Successful Authorization implies that the funds are locked so they can later be captured +--- +# Glossary +For the sake of simplicity, we use the following terms: +* "Sender", "Payer", "Wallet" or "customer VASP" - The customer's wallet. It is the VASP representing the customer. This is the VASP that will send the funds and eventually will submit the on-chain transaction to the blockchain +* "Receiver", "Receiving VASP" or "Merchant VASP" - The VASP representing the merchant which receives the funds (transaction) +* "Customer" - The person who wishes to pay the merchant for goods or services +* "Reference ID", "Common Payment Identifier", "Payment Identifier" - A unique identifier of the payment. It must be a UUID according to RFC4122 with "-"'s included. See also [Reference ID in DIP-1](https://github.com/diem/dip/blob/main/dips/dip-1.mdx#terminology) +--- +# Prerequisite: Sharing Payment Identifier and Receiver Address +A prerequisite for the off-chain negotiations is that both parties (sending and receiving VASPs) have shared a common payment identifier and the receiver address. This common payment identifier is required to support a successive off-chain protocol between the VASPs. +This DIP specifies a couple of examples for exchanging identifiers between VASPs, however, there could be others. The protocol is agnostic to the method of exchanging the common payment identifier. + +See [Appendix A](#appendix-a---prerequisite-sharing-common-payment-identifier-and-address) + +--- +# P2M Off-Chain Protocol Specification +This section specifies P2M payment types, required structures, payloads and expected sequence of calls based on this protocol. + +Content: +* [High Level Flow and Payment Types](#high-level-flow-and-payment-types) +* [Sender / Receiver Interface](#sender--receiver-interface) +* [Request / Response Payload](#request--response-payload) +* [Payment Sequences](#payment-sequences) +* [New Structures](#new-structures) +* [Appendix A: Sharing Common Payment Identifier and Address](#appendix-a---prerequisite-sharing-common-payment-identifier-and-address) +--- +## High-Level Flow and Payment Types +--- +The high-level flow of an eCommerce P2M payment (regardless of the type) is: +1. On the merchant's checkout page the customer chooses to pay with Diem +2. The merchant transmits the payment details to its VASP (receiver VASP) so the latter can generate a payment request with Diem +3. The the receiver VASP generates a payment request with all details needed to identify this payment later in the process, provides it to the merchant and makes it available to the customer's Diem wallet (the customer's VASP or sender) +4. The sender VASP parses the payment request (e.g. by scanning the payment request QR) and uses the provided payment identifier to get the payment details. This is the first step of the off-chain protocol +5. The wallet then presents the requested payment details to the customer so the customer can review the payment +6. The customer approves the payment request in the wallet +7. The wallet initializes the payment protocol (off-chain) which will end (if successful) with the funds being authorized or charged (depending on the payment type). Successful completion implies that the receiving VASP was satisfied with the payer information provided and the sender VASP was satisfied with the merchant information provided. **Note** that if the payment amount exceeds the amount that requires a dual attestation signature to be submitted to the blockchain, it must be part of the on-chain transaction submitted + +### **Charge Payment Type** +Charge payment stipulates an immediate capture of funds. This means that a successful off-chain negotiation for such payment will result in funds being captured. + +As soon as the funds are captured, i.e. the sender agreed to the payment terms, the sender VASP is expected to submit a corresponding transaction on-chain and actually transfer the funds. + + +### **Auth / Capture Payment Type** +Auth / Capture payment, as the name suggests, means that the payment is divided into two steps: +* Authorization +* Capture + +The Authorization step ends when the funds are locked by the sender VASP so they can later be captured by the receiver VASP. Note that the capture may occur several days after a successful authorization. It is up to the receiver VASP to decide on the timing. In addition, the receiver may choose to capture only a partial amount rather than the full amount which was authorized. + +This payment type is useful for delayed fulfillment or pre-authorizing an expected amount to ensure that an amount can be captured after services are rendered. + +Off-chain negotiations for Auth / Capture payment are divided into two steps accordingly. First, off-chain negotiations will result in funds being authorized, i.e. locked by the wallet. Later, a separate off-chain negotiation triggered by the receiving VASP will capture the funds. Note that for the time being, multi-capture is not supported and there can be only a single capture request. + +Successful authorization will not necessarily be followed by a successful capture. It is possible that the receiver will choose to cancel the authorization and essentially void the payment before it was captured. One possible reason to void a payment is that the products for which an amount was locked are out of stock and cannot be fulfilled. + +--- +## Sender / Receiver Interface +--- +This section describes a set of functionalities / activities that should be made available by a Sender and Receiver VASPs in order to support P2M flows based on this DIP. Each functionality can be conceptually compared to an API endpoint. Similar to API documentation, it includes the expected input and output for each functionality. + +## New Command Types +The following is a list of new values for the `command_type` field of the `CommandRequestObject`. Each command type denotes a step in the off-chain negotiations and specifies the data of the requests and the response +* `GetInfoCommand` - Allows obtaining payment information based on reference id +* `InitChargeCommand` - Initializes a payment process of type `charge` +* `InitAuthorizeCommand` - Initializes a payment process of type `auth` +* `ReadyForSettlementNotification` - Notifies the payment is ready for settlement on-chain +* `AuthorizedNotification` - Notifies that payment funds are authorized (locked) for future capture +* `CaptureCommand` - Request to capture authorized (locked) funds +* `VoidCommand` - Voids a payment +* `AbortCommand` - Aborts a payment + +| Command Type |Direction| Description | Request Data | Response Data | +|-|-|-|-|-| +|[GetInfoCommand](#getinfocommand)|Sender > Receiver|By providing the object reference id the wallet can retrieve the object information from the receiving VASP. In most cases, this would be the first command sent by the wallet. The wallet is not expected to provide any payer data at this stage|Reference ID|Payment Details ; Business Data| +|[InitChargeCommand](#initchargecommand)|Sender > Receiver|This functionality allows the wallet to init the payment process based on the payment details. For example, this may occur after the customer approved the payment request|Reference ID ; Payer Data|(optional) Recipient Signature| +|[InitAuthorizeCommand](#initauthorizecommand)|Sender > Receiver|This functionality allows the wallet to init the payment process based on the payment details. For example, this may occur after the customer approved the payment request|Reference ID ; Payer Data|| +|[ReadyForSettlementNotification](#readyforsettlementnotification)|Sender > Receiver|This functionality allows the wallet to indicate the payment is ready for settlement. The wallet is expected to submit a transaction on-chain immediately before or after declaring it is ready|Reference ID|| +|[AuthorizedNotification](#authorizednotification)|Sender > Receiver|This functionality allows the wallet to indicate funds are locked for future capture. Relevant only for Auth / Capture payments|Reference ID|| +|[CaptureCommand](#capturecommand)|Receiver > Sender|This functionality allows the receiver to capture the locked (authorized) funds or part of it. Relevant only for Auth / Capture payments. This optionally includes the recipient signature for dual attestation scenario if needed|Reference ID ; (optional) Amount ; (optional) Recipient Signature|| +|[VoidCommand](#voidcommand)|Receiver > Sender|This functionality allows the receiver to void the payment. For example, the merchant voided the payment because the purchased products are out of stock. Relevant only for Auth / Capture payments|Reference ID|| +|[AbortCommand](#abortcommand)|Sender > Receiver / Receiver > Sender|This functionality allows both parties to abort the payment. For example, the receiver may want to abort the payment following failed risk checks|Reference ID|| + +--- +## Request / Response Payload +--- +The following section describes the request/response payloads of the different functionalities. + +### GetInfoCommand +| Command Type |Direction| Description | Request Data | Response Data | +|-|-|-|-|-| +GetInfoCommand|Sender > Receiver|By providing the object reference id the wallet can retrieve the object information from the receiving VASP. In most cases, this would be the first command sent by the wallet. The wallet is not expected to provide any payer data at this stage|Reference ID|Payment Details ; Business Data| + +#### GetInfoCommand Request +Field|Type|Required?|Description| +|-|-|-|-| +|reference_id|str|Y|Unique reference ID of this payment. This value should be globally unique| + +``` +{ + "_ObjectType": "CommandRequestObject", + "command_type": "GetInfoCommand", + "cid": "3185027f-0574-6f55-2668-3a38fdb5de98", + "command": { + "_ObjectType": "GetInfoCommand", + "reference_id": "4185027f-0574-6f55-2668-3a38fdb5de98" + } +} +``` +#### GetInfoCommand Response +Field|Type|Required?|Description| +|-|-|-|-| +|payment_info|[PaymentInfoObject](#PaymentInfoObject)|Y|The minimal payment details required in order for the payer to decide whether to accept the payment or reject it.| + +``` +{ + "_ObjectType": "CommandResponseObject", + "status": "success", + "cid": "12ce83f6-6d18-0d6e-08b6-c00fdbbf085a", + "result": { + "_ObjectType": "GetInfoCommandResponse", + "payment_info": { + "receiver": { + "account_address": "dm1pllhdmn9m42vcsamx24zrxgs3qqqpzg3ng32kvacapx5fl", + "business_data": { + "name": "Acme", + "legal_name": "Acme Technologies Inc.", + "address": { + "city": "San Francisco", + "country": "US", + "line1": "1260 Market Street", + "line2": "Suite 450", + "postal_code": "94103", + "state": "CA" + } + } + }, + "action": { + "amount": "100000000", + "currency": "XUS", + "action": "charge", + "timestamp": "72322" + }, + "reference_id": "4185027f-0574-6f55-2668-3a38fdb5de98", + "description": "payment request description" + } + } +} +``` +### InitChargeCommand +| Command Type |Direction| Description | Request Data | Response Data | +|-|-|-|-|-| +|InitChargeCommand|Sender > Receiver|This functionality allows the wallet to init the payment process for payments of type `charge` based on the payment details. For example, this may occur after the customer approved the payment request|Reference ID ; Payer Data|| + +#### InitChargeCommand Request +Field|Type|Required?|Description| +|-|-|-|-| +|sender|[PaymentSenderObject](#paymentsenderobject)|Y|Minimal payer information required for the receiving VASP to enforce AML and perform risk checks| +|reference_id|str|Y|Unique reference ID of this payment. This value should be globally unique| + +``` +{ + "_ObjectType": "CommandRequestObject", + "command_type": "InitChargeCommand", + "cid": "3185027f-0574-6f55-2668-3a38fdb5de98", + "command": { + "_ObjectType": "InitChargeCommand", + "sender": { + "account_address": "dm1pg9q5zs2pg9q5zs2pg9q5zs2pg9skzctpv9skzcg9kmwta", + "payer_data": { + "given_name": "Alice", + "surname": "Cooper", + "address": { + "city": "Sunnyvale", + "country": "US", + "line1": "1234 Maple Street", + "line2": "Apartment 123", + "postal_code": "12345", + "state": "California" + }, + "national_id_data": { + "id_value": "123456789", + "country": "US", + "type": "SSN" + } + } + }, + "reference_id": "4185027f-0574-6f55-2668-3a38fdb5de98" + } +} + +``` +#### InitChargeCommand Response +Field|Type|Required?|Description| +|-|-|-|-| +| recipient_signature | str | N | Signature of the recipient of this transaction encoded in hex. A metadata payload is signed with the compliance key of the recipient VASP and is used for on-chain attestation from the recipient party. This may be omitted on Blockchains which do not require on-chain attestation. Generated via [Recipient Signature](https://github.com/diem/dip/blob/main/dips/dip-1.mdx#recipient-compliance-signature). | + +``` +{ + "_ObjectType": "CommandResponseObject", + "status": "success", + "cid": "12ce83f6-6d18-0d6e-08b6-c00fdbbf085a", + "result": { + "_ObjectType": "InitChargeCommandResponse", + "recipient_signature": "..." + } +} + +``` +### InitAuthorizeCommand +| Command Type |Direction| Description | Request Data | Response Data | +|-|-|-|-|-| +|InitChargeCommand|Sender > Receiver|This functionality allows the wallet to init the payment process for payments of type `auth` based on the payment details. For example, this may occur after the customer approved the payment request|Reference ID ; Payer Data|| + +#### InitAuthorizeCommand Request +Field|Type|Required?|Description| +|-|-|-|-| +|sender|[PaymentSenderObject](#paymentsenderobject)|Y|Minimal payer information required for the receiving VASP to enforce AML and perform risk checks| +|reference_id|str|Y|Unique reference ID of this payment. This value should be globally unique| + +``` +{ + "_ObjectType": "CommandRequestObject", + "command_type": "InitAuthorizeCommand", + "cid": "3185027f-0574-6f55-2668-3a38fdb5de98", + "command": { + "_ObjectType": "InitAuthorizeCommand", + "sender": { + "account_address": "dm1pg9q5zs2pg9q5zs2pg9q5zs2pg9skzctpv9skzcg9kmwta", + "payer_data": { + "given_name": "Alice", + "surname": "Cooper", + "address": { + "city": "Sunnyvale", + "country": "US", + "line1": "1234 Maple Street", + "line2": "Apartment 123", + "postal_code": "12345", + "state": "California" + }, + "national_id_data": { + "id_value": "123456789", + "country": "US", + "type": "SSN" + } + } + }, + "reference_id": "4185027f-0574-6f55-2668-3a38fdb5de98" + } +} + +``` +#### InitAuthorizeCommand Response +``` +{ + "_ObjectType": "CommandResponseObject", + "status": "success", + "cid": "12ce83f6-6d18-0d6e-08b6-c00fdbbf085a" +} + +``` + +### ReadyForSettlementNotification +| Command Type |Direction| Description | Request Data | Response Data | +|-|-|-|-|-| +|ReadyForSettlementNotification|Sender > Receiver|This functionality allows the wallet to indicate the payment is ready for settlement. The wallet is expected to submit a transaction on-chain immediately before or after declaring it is ready|Reference ID|| + +#### ReadyForSettlementNotification Request +Field|Type|Required?|Description| +|-|-|-|-| +|reference_id|str|Y|Unique reference ID of this payment. This value should be globally unique| +``` +{ + "_ObjectType": "CommandRequestObject", + "command_type": "ReadyForSettlementNotification", + "cid": "3185027f-0574-6f55-2668-3a38fdb5de98", + "command": { + "_ObjectType": "ReadyForSettlementNotification", + "reference_id": "4185027f-0574-6f55-2668-3a38fdb5de98" + } +} + +``` +#### ReadyForSettlementNotification Response +``` +{ + "_ObjectType": "CommandResponseObject", + "status": "success", + "cid": "12ce83f6-6d18-0d6e-08b6-c00fdbbf085a" +} +``` + +### AuthorizedNotification +| Command Type |Direction| Description | Request Data | Response Data | +|-|-|-|-|-| +|AuthorizedNotification|Sender > Receiver|This functionality allows the wallet to indicate funds are locked for future capture. Relevant only for Auth / Capture payments|Reference ID| + +#### AuthorizedNotification Request +Field|Type|Required?|Description| +|-|-|-|-| +|reference_id|str|Y|Unique reference ID of this payment. This value should be globally unique| +``` +{ + "_ObjectType": "CommandRequestObject", + "command_type": "AuthorizedNotification", + "cid": "3185027f-0574-6f55-2668-3a38fdb5de98", + "command": { + "_ObjectType": "AuthorizedNotification", + "reference_id": "4185027f-0574-6f55-2668-3a38fdb5de98" + } +} + +``` +#### AuthorizedNotification Response +``` +{ + "_ObjectType": "CommandResponseObject", + "status": "success", + "cid": "12ce83f6-6d18-0d6e-08b6-c00fdbbf085a" +} +``` + +### CaptureCommand +| Command Type |Direction| Description | Request Data | Response Data | +|-|-|-|-|-| +|CaptureCommand|Receiver > Sender|This functionality allows the receiver to capture the locked (authorized) funds or part of it. Relevant only for Auth / Capture payments|Reference ID ; (optional) Amount ; (optional) Recipient Signature|| + +#### CaptureCommand Request +Field|Type|Required?|Description| +|-|-|-|-| +|reference_id|str|Y|Unique reference ID of this payment. This value should be globally unique| +| amount | uint | N | Amount to capture. Base units are the same as for on-chain transactions for this currency. Amount can equal or lower than the authorized amount| +| currency | enum | N | One of the supported on-chain currency types, e.g. XUS. Must be the same as the authorized currency | +|recipient_signature|str|N| Signature of the recipient of this transaction encoded in hex. A metadata payload is signed with the compliance key of the recipient VASP and is used for on-chain attestation from the recipient party. This may be omitted on Blockchains which do not require on-chain attestation. Generated via [Recipient Signature](https://github.com/diem/dip/blob/main/dips/dip-1.mdx#recipient-compliance-signature)| + +``` +{ + "_ObjectType": "CommandRequestObject", + "command_type": "CaptureCommand", + "cid": "3185027f-0574-6f55-2668-3a38fdb5de98", + "command": { + "_ObjectType": "CaptureCommand", + "reference_id": "4185027f-0574-6f55-2668-3a38fdb5de98", + "recipient_signature": "..." + } +} + +``` +#### CaptureCommand Response +``` +{ + "_ObjectType": "CommandResponseObject", + "status": "success", + "cid": "12ce83f6-6d18-0d6e-08b6-c00fdbbf085a" +} +``` + +### VoidCommand +| Command Type |Direction| Description | Request Data | Response Data | +|-|-|-|-|-| +|VoidCommand|Receiver > Sender|This functionality allows the receiver to void the payment. For example, the merchant voided the payment because the purchased products are out of stock. Relevant only for Auth / Capture payments|Reference ID|| + +#### VoidCommand Request +Field|Type|Required?|Description| +|-|-|-|-| +|reference_id|str|Y|Unique reference ID of this payment. This value should be globally unique| +|void_message|str|N|Additional information about the reason for voiding this payment| +``` +{ + "_ObjectType": "CommandRequestObject", + "command_type": "VoidCommand", + "cid": "3185027f-0574-6f55-2668-3a38fdb5de98", + "command": { + "_ObjectType": "VoidCommand", + "reference_id": "4185027f-0574-6f55-2668-3a38fdb5de98", + "void_message": "void message" + } +} +``` +#### VoidCommand Response +``` +{ + "_ObjectType": "CommandResponseObject", + "status": "success", + "cid": "12ce83f6-6d18-0d6e-08b6-c00fdbbf085a" +} +``` +### AbortCommand +| Command Type |Direction| Description | Request Data | Response Data | +|-|-|-|-|-| +|AbortCommand|Sender > Receiver / Receiver > Sender|This functionality allows both parties to abort the payment. For example, the receiver may want to abort the payment following failed risk checks|Reference ID|| + + +#### AbortCommand Request +Field|Type|Required?|Description| +|-|-|-|-| +|reference_id|str|Y|Unique reference ID of this payment. This value should be globally unique| +|abort_message|str|N|Additional information about the reason for aborting this payment| +``` +{ + "_ObjectType": "CommandRequestObject", + "command_type": "AbortCommand", + "cid": "3185027f-0574-6f55-2668-3a38fdb5de98", + "command": { + "_ObjectType": "AbortCommand", + "reference_id": "4185027f-0574-6f55-2668-3a38fdb5de98", + "abort_message": "abort message" + } +} + +``` +#### AbortCommand Response +``` +{ + "_ObjectType": "CommandResponseObject", + "status": "success", + "cid": "12ce83f6-6d18-0d6e-08b6-c00fdbbf085a" +} + +``` + +--- +## Payment Sequences +--- +This section describes the expected sequences in P2M scenarios. +Each scenario is expressed using a sequence of Sender / Receiver functionality. + +**Reminder:** Before this scenario starts, an exchange of a common payment identifier (Reference ID) must be completed. + +### Charge Sequence +| Step | Command Type | Triggered by | Description | +|-|-|-|-| +|1|GetInfoCommand|Sender|The wallet requests payment details using the reference id provided. The receiver will respond with the payment details and merchant data. The wallet will display the payment details so the customer can approve or reject| +|2|InitChargeCommand|Sender|Following customer approval the wallet initializes the payment process and sends the payer data| +|3|ReadyForSettlementNotification|Sender|The wallet indicates the payment is ready for settlement. The wallet is expected to submit a transaction on-chain immediately before or after this step| + +#### Charge Sequence Diagram +![Charge Sequence Diagram](https://static.swimlanes.io/e8ce8614b7c53cdbc2e04e3a88629868.png) + +### Authorization Sequence (Auth / Capture) +| Step | Command Type | Triggered by | Description | +|-|-|-|-| +|1|GetInfoCommand|Sender|The wallet requests payment details using the reference id provided. The receiver will respond with the payment details and merchant data. The wallet will display the payment details so the customer can approve or reject| +|2|InitAuthorizeCommand|Sender|Following customer approval the wallet initializes the payment process and sends the payer data| +|3|AuthorizedNotification|Sender|The wallet indicates the funds are locked for a future capture| + +#### Authorization Sequence Diagram +![Authorize Sequence Diagram](https://static.swimlanes.io/093a776384fcc144e5dca8576179e107.png) + +### Capture Sequence (Auth / Capture) +IMPORTANT: Successful authorization is a precondition to capture +| Step |Command Type|Triggered by | Description | +|-|-|-|-| +|1|CaptureCommand|Receiver|The receiver requests to capture the authorized funds or part of it| +|2|ReadyForSettlementNotification|Sender|The wallet indicates the payment is ready for settlement. The wallet is expected to submit a transaction on-chain immediately before or after this step| + +#### Capture Sequence Diagram +![Capture Sequence Diagram](https://static.swimlanes.io/1c620cc1c00c1ef0dd4b224ac953ba2f.png) + +### Void Sequence (Auth / Capture) +IMPORTANT: Successful authorization is a precondition to void +| Step |Command Type|Triggered by | Description | +|-|-|-|-| +|1|VoidCommand|Receiver|Receiving VASP wants to void the payment. For example, the merchant voided the payment because the purchased products are out of stock| + +### Risk Checks Failed (Init Payment Error) +| Step |Command Type| Triggered by | Description | +|-|-|-|-| +|1|GetInfoCommand|Sender|The wallet requests payment details using the reference id provided. The receiver will respond with the payment details and merchant data. The wallet will display the payment details so the customer can approve or reject| +|2|InitChargeCommand / InitAuthorizeCommand|Sender|Following customer approval the wallet initializes the payment process and sends the payer data. Risk checks failed. Receiver will respond with PaymentCommandErrorObject where the error_code is `risk_checks_failed`| + +### Invalid Command Type (Premature Capture Attempt Error) +| Step |Command Type| Triggered by | Description | +|-|-|-|-| +|1|GetInfoCommand|Sender|The wallet requests payment details using the reference id provided. The receiver will respond with the payment details and merchant data. The wallet will display the payment details so the customer can approve or reject| +|2|CaptureCommand|Sender|Instead of initializing the payment, the wallet wrongly attempts to capture the payment. Receiver will respond with PaymentCommandErrorObject where the error_code is `invalid_command_type`| + +### Payment Type Mismatch (Init Payment Error) +| Step |Command Type| Triggered by | Description | +|-|-|-|-| +|1|GetInfoCommand|Sender|The wallet requests payment details using the reference id provided. The receiver will respond with the `charge` payment details and merchant data. The wallet will display the payment details so the customer can approve or reject| +|2|InitAuthorizeCommand|Sender|Following customer approval the wallet attempts to initialize an `auth` payment. Receiver will respond with PaymentCommandErrorObject where the error_code is `payment_type_mismatch`| + +### Abort Sequence - Customer Rejected the Payment Request +| Step |Command Type| Triggered by | Description | +|-|-|-|-| +|1|GetInfoCommand|Sender|The wallet requests payment details using the reference id provided. The receiver will respond with the payment details and merchant data. The wallet will display the payment details so the customer can approve or reject| +|2|AbortCommand|Sender|Customer decided not to approve the payment based on the information displayed. It is recommended to include a descriptive abort_message| + +### Abort Sequence - Merchant Data Checks (performed by the Sender) Failed +| Step |Command Type| Triggered by | Description | +|-|-|-|-| +|1|GetInfoCommand|Sender|The wallet requests payment details using the reference id provided. The receiver will respond with the payment details and merchant data. The wallet will display the payment details so the customer can approve or reject| +|2|InitChargeCommand / InitAuthorizeCommand|Sender|Following customer approval the wallet initializes the payment process and sends the payer data| +|3|AbortCommand|Sender|Sender decided to abort. Can happen if the sender does not approve merchant data provided by the receiver| + +--- +## New Structures +--- +This section introduces new structures required for P2M scenarios. +## PaymentInfoObject +The minimal payment details required in order for the payer to decide whether to accept the payment or reject it. + +Field|Type|Required?|Description| +|-|-|-|-| +|receiver|[PaymentReceiverObject](#paymentreceiverobject)|Y|The details of the payment receiver (e.g. merchant)| +|action|[PaymentActionObject](#paymentactionobject)|Y|Information regarding the type of payment and payment amount| +|reference_id|str|Y|Unique reference ID of this payment. This value should be globally unique| +|description|str|N|Description of the payment. To be displayed to the customer| + +``` +"payment_info": +{ + "receiver": { + PaymentReceiverObject() + }, + "action": { + PaymentActionObject() + }, + "reference_id": "4185027f-0574-6f55-2668-3a38fdb5de98", + "description": "payment request description" +} +``` + +## PaymentReceiverObject +A PaymentReceiverObject represents the business/merchant in the payment. In P2M scenarios it is the receiver of funds. + +|Field|Type|Required?|Description| +|-|-|-|-| +| account_address | str | Y | Address of the receiver account. The addresses are encoded using bech32| +|business_data|[BusinessDataObject](#businessdataobject)|Y|It includes the details of the merchant to be displayed to the payer| + +``` +"receiver": { + "account_address": "dm1pllhdmn9m42vcsamx24zrxgs3qqqpzg3ng32kvacapx5fl", + "business_data": { + BusinessDataObject() + } +} +``` +## PaymentSenderObject +A PaymentSenderObject represents the person in the payment. In P2M scenarios it is the sender of funds. + +|Field|Type|Required?|Description| +|-|-|-|-| +| account_address | str | Y | Address of the receiver account. The addresses are encoded using bech32| +|payer_data|[PayerDataObject](#payerdataobject)|Y|The details of the payer| + +``` +"sender": { + "account_address": "dm1pg9q5zs2pg9q5zs2pg9q5zs2pg9skzctpv9skzcg9kmwta", + "payer_data": { + PayerDataObject() + } +} +``` + +## PayerDataObject +The details of the payer. payer_data is the minimal set of payer information required by the merchant to perform risk checks and apply AML policies. +|Field|Type|Required?|Description| +|-|-|-|-| +|given_name|str|Y|Legal given name of the payer| +|surname|str|Y|Legal surname of the payer| +|address|[AddressObject](#addressobject)|Y|Merchant's physical address information| +|national_id|[NationalIdObject](#nationalidobject)|N|National ID information for payer. e.g. SSN| + +``` +"payer_data": +{ + "name": "Alice", + "surname": "Cooper", + "address": { + AddressObject() + }, + "national_id": { + NationalIdObject() + } +} +``` +## BusinessDataObject +The details of the merchant. +|Field|Type|Required?|Description| +|-|-|-|-| +|name|str|Y|Merchant's display name. Should be recognizable by the payer| +|legal_name|str|Y|The legal entity name| +|image_url|str|N|URL with the business logo| +|address|[AddressObject](#addressobject)|Y|Merchant's physical address information| +``` +"business_data": +{ + "name": "Acme", + "legal_name": "Acme Technologies Inc.", + "address": { + AddressObject() + } +} +``` + +## PaymentActionObject +Information about the payment type (i.e. action), amount and currency. + +|Field|Type|Required?|Description| +|-----|----|---------|-----------| +| amount | uint | Y | Amount of the transfer. Base units are the same as for on-chain transactions for this currency. For example, if DiemUSD is represented on-chain where "1" equals 1e-6 dollars, then "1" equals the same amount here. For any currency, the on-chain mapping must be used for amounts. | +| currency | enum | Y | One of the supported on-chain currency types, e.g. XUS| +| action | [Action enum](#action-enum) | Y | Populated in the request. This value indicates the requested action to perform. For immediate capture and P2P transfer, "charge" is used. For auth and capture, "auth" and "capture" should be used. "capture" can only be performed after a valid "auth" | +| valid_until | uint | N | Unix timestamp indicating until when the payment can be authorized. Once this time has been reached, an attempt to authorize and lock the funds will be rejected. One should consider limiting this period to several minutes (e.g. 30 minutes) which should suffice for the customer to approve or reject the payment request| +| timestamp | uint | Y | Unix timestamp indicating the time that the payment Command was created.| + +``` +{ + "amount": 100000000, + "currency": "XUS", + "action": "auth", + "valid_until": 74000, + "timestamp": 72322, +} +``` +### Action Enum +The following values are allowed for the Action Enum +* `charge` +* `auth` +* `capture` + +## AddressObject +Represents a physical address +| Field | Type | Required? | Description | +|-|-|-|-| +| city | str | Y | The city, district, suburb, town, or village | +| country | str | Y | Two-letter country code (https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) | +| line1 | str | Y | Address line 1 | +| line2 | str | N | Address line 2 - apartment, unit, etc.| +| postal_code| str | Y | ZIP or postal code | +| state | str | N | State, county, province, region.| +``` +{ + "city": "San Francisco", + "country": "US", + "line1": "1260 Market Street", + "line2": "Suite 450", + "postal_code": "94103", + "state": "CA", +} +``` +## NationalIdObject +Represents a national ID. + +| Field | Type | Required? | Description | +|-|-|-|-| +| id_value | str | Y | Indicates the national ID value - for example, a social security number | +| country | str | N | Two-letter country code (https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) | +| type | str | N | Indicates the type of the ID | + +``` +{ + "id_value": "123-45-6789", + "country": "US", + "type": "SSN", +} +``` +## PaymentCommandErrorObject +This element is used to indicate that the command execution ended with an error. Successful command executions must NOT include this element. + +| Field | Type | Required? | Description | +|-|-|-|-| +| error_code |[ErrorCode enum](#errorcode-enum)| N | This field is used to specify the reason for the error| +| error_message | str | N | Additional details about this error| +``` +"payment_command_error": { + "error_code": "reference_id_not_found", + "error_message": "Payment reference id was not found" +} +``` +This element can be added to any command response under the result object (if the command execution was not successful). +For example, a response of GetInfoCommand in which the refernce_id provided was not found by the receiver: +``` +{ + "_ObjectType": "CommandResponseObject", + "status": "success", + "cid": "12ce83f6-6d18-0d6e-08b6-c00fdbbf085a", + "result": { + "_ObjectType": "PaymentCommandErrorObject", + "payment_command_error": { + "error_code": "reference_id_not_found", + "error_message": "Payment reference id was not found" + } + } +} +``` +### ErrorCode Enum +* `reference_id_not_found` +* `risk_checks_failed` +* `missing_information` +* `payment_type_mismatch` +* `invalid_command_type` +* `unspecified_error` + + +--- +# Appendix A - Prerequisite: Sharing Common Payment Identifier and Address + +Note that this is a prerequisite for the off-chain negotiations and not a part of it. + +## Acquire Payment Identifier and Receiver Address Using Redirect URL + +This URL contains the payment reference id (i.e. the common payment identifier) and the wallet's website address. + +The redirect URL is likely to be used in the **Web Redirect** flow which means that the customer will be redirected from the merchant's checkout page to the wallet's website to review and approve the payment. + +The table below specifies the fields that should be encoded into a series of URL parameters appended to the query string. + +|Field|Type|Required?|Description| +|-|-|-|-| +|vasp_address|str|Y|Address of receiving VASP. The address is encoded using bech32. For Diem addresses format, refer to the "account identifiers" section in [DIP-5](https://dip.diem.com/dip-5/#account-identifiers).| +|reference_id|str|Y|A unique identifier of this payment. It should be a UUID according to RFC4122 with "-"'s included.| +|redirect_url|str|N|Encoded URL used by the wallet to redirect the customer back to the merchant| + +For example, for the following values: +|Field|Value| +|-|-| +|vasp_address|dm1pgxah7pxhzljvp3p4m9g0m3tm0qqqqqqqqqqqqqqgyftgh| +|reference_id|ad8d888a-1791-4b63-98e5-6f1d6ddb4411| +|redirect_url|http%3A%2F%2Fsomemerchantnname.com%2Fcallbackpage| + + +The URL format would be: + +`https://some-diem-wallet.com/pay?vasp_address=dm1pgxah7pxhzljvp3p4m9g0m3tm0qqqqqqqqqqqqqqgyftgh&reference_id=ad8d888a-1791-4b63-98e5-6f1d6ddb4411&redirect_url=http%3A%2F%2Fsomemerchantnname.com%2Fcallbackpage` + + +After parsing this request, the wallet can request the payment details from the receiving VASP and display them to the customer to review and approve. + +## Acquire Payment Identifier and Receiver Address Using QR Code + +Similar to the URL, the QR code contains the payment reference id (i.e. the common payment identifier). + +The QR Code is likely to be used in the **QR Scan** flow, which means that the customer will use the wallet app on the mobile device to scan the QR code on the merchant's checkout page. Following the QR code scan, the wallet app will display the payment details for the customer to review and approve the payment. + +The QR code is a representation of a deeplink. +The table below specifies the fields that should be encoded into the QR code deeplink. + +|Field|Type|Required?|Description| +|-----|----|---------|-----------| +|vasp_address|str|Y|Address of receiving VASP. The address is encoded using bech32. For Diem addresses format, refer to the "account identifiers" section in [DIP-5](https://dip.diem.com/dip-5/#account-identifiers).| +|reference_id|str|Y|A unique identifier of this payment. It should be a UUID according to RFC4122 with "-"'s included.| +|redirect_url|str|N|Encoded URL used by the wallet to redirect the customer back to the merchant| + +For example, for the following values: + +|Field|Value| +|-|-| +|vasp_address|dm1pgxah7pxhzljvp3p4m9g0m3tm0qqqqqqqqqqqqqqgyftgh| +|reference_id|ad8d888a-1791-4b63-98e5-6f1d6ddb4411| + +The deeplink represented by QR code would be (the domain and path are examples - the real wallet domain/path should be used): + +`diem://some-diem-wallet.com/pay?vasp_address=dm1pgxah7pxhzljvp3p4m9g0m3tm0qqqqqqqqqqqqqqgyftgh&reference_id=ad8d888a-1791-4b63-98e5-6f1d6ddb4411` + +After scanning the QR code, the wallet can request the payment details from the receiving VASP and display them to the customer to review and approve.