# 3rd Party Payment Initiation ## Use Case - **NicePayment** is a payment service that wishes to connect to Alice's Open Payments account - **Alice** is an account holder at Fynbos and a user at NicePayment - **OnlineShop** and **Shoppex** are online merchants - **Fynbos** is Alice's account provider - **Uphold** is NicePayment's account provider - Alice wishes to connect her Fynbos account to her NicePayment account in order to be able to pay with NicePayment - OnlineShop and Shoppex do not have an Open Payments account. They are customers of NicePayment, which handles incoming payments on their behalf. ### Out of Scope - How do OnlineShop and Shoppex interact with NicePayment to - check that a payment has completed - withdraw their money ### Step 1: Alice connects her Fynbos account to NicePayment Alice wishes to give NicePayment the ability to send payments from her account to an arbitrary receiver. Alice provides NicePayment with her Payment Pointer: `https://fynbos.me/alice` NicePayment queries the Payment Pointer to get the grant request endpoint URL. ```http GET /alice HTTP/1.1 Host: fynbos.me Accept: application/op-account-v1+json ``` Fynbos responds: ```http HTTP/1.1 200 Success Content-Type: application/op-alice-v1+json { "id": "https://fynbos.me/alice", "publicName": "Alice" "assetCode": "USD", "assetScale": 2 "authServer": "https://fynbos.dev/auth" } ``` NicePayment makes a Grant Request to Fynbos at `https://fynbos.dev/auth`: ```json { "access_token": { "access": [ { "type": "outgoing-payment", "actions": [ "create", "authorize" ], "locations": [ "https://fynbos.me/alice" ] } ] }, "client": { "display": { "name": "NicePayment", "uri": "https://nicepayment.com" }, "key": { "proof": "httpsig", "jwk": { "kty": "RSA", "e": "AQAB", "kid": "xyz-1", "alg": "RS256", "n": "kOB5rR4Jv0GMeL...." } } }, "interact": { "start": [ "redirect" ], "finish": { "method": "redirect", "uri": "https://nicepayment.com/return/876FGRD8VC", "nonce": "LKLTI25DK82FX4T4QFZC" } } } ``` Fynbos sends back an instruction to NicePayment to redirect Alice to a Fynbos web page to authenticate herself and consent to the grant request. ```json { "interact": { "redirect": "https://fynbos.dev/auth/4CF492MLVMSW9MKMXKHQ", "finish": "MBDOFXG4Y5CVJCX821LH" }, "continue": { "access_token": { "value": "80UPRY5NM33OMUKMKSKU" }, "uri": "https://fynbos.dev/auth/continue" } } ``` NicePayment directs Alice to `https://fynbos.dev/auth/4CF492MLVMSW9MKMXKHQ` which is a web page hosted by Fynbos. Fynbos authenticates Alice and then prompts Alice to consent to giving NicePayment access to her account. Fynbos allows Alice to specify certain limits, e.g. - maximum amount - cadence - start day - end day ![](https://i.imgur.com/supfsq2.png) Fynbos redirects Alice to `https://nicepayment.com/return/876FGRD8VC?hash=p28jsq0Y2KK3WS__a42tavNC64ldGTBroywsWxT4md_jZQ1R2HZT8BOWYHcLmObM7XHPAdJzTZMtKBsaraJ64A &interact_ref=4IFWWIKYBC2PQ6U56NL1` - The URL was provided by NicePayment in the grant request - The `interact_ref` is a random value generated by Fynbos - The `hash` was calculated by Fynbos based on: - the `nonce` NicePayment provided in the original request (`LKLTI25DK82FX4T4QFZC`), - the `nonce` Fynbos provided in the response (`MBDOFXG4Y5CVJCX821LH`) - the `interact_ref`, and - the URL of the grant request endpoint `https://fynbos.dev/auth`. NicePayment validates the hash and then makes a continue request to Fynbos at `https://fynbos.dev/auth/continue` using the access token `80UPRY5NM33OMUKMKSKU`. Fynbos responds to NicePayment with the following: ```json { "access_token": { "value": "OS9M2PMHKUR64TB8N6BW7OZB8CDFONP219RP1LT0", "manage": "https://fynbos.dev/auth/token/PRY5NM33OM4TB8N6BW7", "expires_in": 762534, "access": [ { "type": "outgoing-payment", "actions": [ "create", "authorize", ], "locations": [ "https://fynbos.me/alice" ], "limits": { "startAt": "2022-05-13T18:25:43.511Z", "expiresAt": "2023-05-13T18:25:43.511Z", "interval": "P1D", "sendAmount": { "amount": 10000, "assetCode": "USD", "assetScale": 2 } } }] }, "continue": { "access_token": { "value": "YT98RY5NM33OMUKMK65U" }, "uri": "https://fynbos.dev/auth/continue" } } ``` NicePayment now has a grant to create and authorize outgoing payments from Alice's account up to a value of 100 USD per day. ### Step 2: Alice pays with NicePayment #### Case 1: Alice does not exceed her limit Alice wants to purchase an item for 40 USD at OnlineShop. On checkout, she decides to pay with NicePayment. NicePayment creates an incoming payment for the purchase on their own account, referencing it to OnlineShop, Alice, and a transaction. ```http POST /nicepayment HTTP/1.1 Host: uphold.com Content-Type: application/op-incoming-payment-v1+json Accept: application/op-incoming-payment-v1+json Signature-Input: gnap=("@request-target" "host" "authorization");created=1624564850;keyid="xyz-client" Signature: gnap=:EN/rExQ/knVi61P5AFhyMGN7aVPzk/9C7nsYAWF2RvzsoV1uNxGZklM55qCIQpuhoNty4EhiH7iwuzZBbRCQcQ==: Authorization: GNAP M2PMHKUR64TB8N6BW7OZB8CDFONP219RP1LT0OS9 { "incomingAmount": { "amount": "4000", "assetCode": "USD", "assetScale": 2 }, "externalRef": "{\"receiveId\":\"149225\",\"sendId\":\"382076\",\"txId\":\"14cf7bfe-370a-4550-9b8e-d9d953ab21b1\"}" } ``` Uphold has checked that NicePayment is authorised to create the payment. It returns a response: ```http HTTP/1.1 201 Created Content-Type: application/op-incoming-payment-v1+json { "id": "https://uphold.com/nicepayment/fi7td6dito8yf6t" "accountId": "https://uphold.com/nicepayment", "state": "pending", "incomingAmount": { "amount": 4000, "assetCode": "EUR", "assetScale": 2 }, "externalRef": "{\"receiveId\":\"149225\",\"sendId\":\"382076\",\"txId\":\"14cf7bfe-370a-4550-9b8e-d9d953ab21b1\"}" } ``` Next, NicePayment creates the outgoing payment at Alice's Payment Pointer: ```http POST /alice HTTP/1.1 Host: fynbos.me Content-Type: application/op-outgoing-payment-v1+json Accept: application/op-outgoing-payment-v1+json Signature-Input: gnap=("@request-target" "host" "authorization");created=1624564850;keyid="xyz-client" Signature: gnap=:EN/rExQ/knVi61P5AFhyMGN7aVPzk/9C7nsYAWF2RvzsoV1uNxGZklM55qCIQpuhoNty4EhiH7iwuzZBbRCQcQ==: Authorization: GNAP OS9M2PMHKUR64TB8N6BW7OZB8CDFONP219RP1LT0 { "state": "authorized", "receivingPayment": "https://uphold.com/nicepayment/fi7td6dito8yf6t" } ``` Within Fynbos the resource server (Rafiki) checks the grant associated with the provided token and gets the following response. ```json { "active": true, "grant": "PRY5NM33OM4TB8N6BW7", "access": [ { "type": "outgoing-payment", "actions": [ "create", "authorize", ], "locations": [ "https://fynbos.me/alice" ], "limits": { "startAt": "2022-05-13T18:25:43.511Z", "expiresAt": "2023-05-13T18:25:43.511Z", "interval": "P1D", "sendAmount": { "amount": 10000, "assetCode": "USD", "assetScale": 2 } } } ], "key": { "proof": "httpsig", "jwk": { "kty": "RSA", "e": "AQAB", "kid": "xyz-1", "alg": "RS256", "n": "kOB5rR4Jv0GMeL...." } }, } ``` Rafiki, the RS, keeps balances for each time interval for every `grant`. Even though the AS confirms that this access token is valid, the RS may decline the client's request if the amount that it wants to send exceeds the limit per interval. In this case, Fynbos has no record of any transactions using this grant today so it starts a new balance to track these. The balance of `PRY5NM33OM4TB8N6BW7_2022-07-17` is 0 USD and will go up to 40 USD + fees if this payment completes. Fynbos has checked that NicePayment is authorised to create the payment. It returns a response: ```http HTTP/1.1 201 Created Content-Type: application/op-outgoing-payment-v1+json { "id": "https://fynbos.me/alice/6tfi7td6dito8yf" "accountId": "https://fynbos.me/alice/", "state": "authorized", "receivingPayment": "https://uphold.com/nicepayment/fi7td6dito8yf6t" } ``` Since the payment is authorised, Fynbos will automatically start sending the money to `https://uphold.com/nicepayment/fi7td6dito8yf6t`. Therefore, it will query the receiving endpoint ```http GET /nicepayment/fi7td6dito8yf6t HTTP/1.1 Host: uphold.com Accept: application/op-incoming-payment-v1+json ``` and receive the incoming payment details including STREAM credentials ```http HTTP/1.1 200 OK Content-Type: application/op-incoming-payment-v1+json { "id": "https://uphold.com/nicepayment/fi7td6dito8yf6t", "accountId": "https://uphold.com/nicepayment", "state": "pending", "incomingAmount": { "amount": 4000, "assetCode": "EUR", "assetScale": 2 }, "externalRef": "{\"receiveId\":\"149225\",\"sendId\":\"382076\",\"txId\":\"14cf7bfe-370a-4550-9b8e-d9d953ab21b1\"}", "ilpAddress": "g.uphold.Cty6C+YB5X9FhSOUPCL", "sharedSecret": "6jR5iNIVRvqeasJeCty6C+YB5X9FhSOUPCL/5nha5Vs=", "receiptsEnabled": false } ``` Fynbos opens a STREAM connection and starts sending until Uphold has received 40 USD into NicePayment's account and does not accept any more packets. Alice is redirected to the order confirmation screen at OnlineShop. #### Case 2: Alice exceeds her limit Alice wants to purchase another item for 100 USD at Shoppex. She has already spent 40 USD on an item from OnlineShop today. On checkout, she decides to pay with NicePayment. NicePayment creates an incoming payment for the purchase on their own account, referencing it to Shoppex, Alice, and a transaction. ```http POST /nicepayment HTTP/1.1 Host: uphold.com Content-Type: application/op-incoming-payment-v1+json Accept: application/op-incoming-payment-v1+json Signature-Input: gnap=("@request-target" "host" "authorization");created=1624564850;keyid="xyz-client" Signature: gnap=:EN/rExQ/knVi61P5AFhyMGN7aVPzk/9C7nsYAWF2RvzsoV1uNxGZklM55qCIQpuhoNty4EhiH7iwuzZBbRCQcQ==: Authorization: GNAP M2PMHKUR64TB8N6BW7OZB8CDFONP219RP1LT0OS9 { "incomingAmount": { "amount": "10000", "assetCode": "USD", "assetScale": 2 }, "externalRef": "{\"receiveId\":\"187344\",\"sendId\":\"382076\",\"txId\":\"6c8f1bbf-af89-4ea2-b79c-8af38dfa69e8\"}" } ``` Uphold has checked that NicePayment is authorised to create the payment. It returns a response: ```http HTTP/1.1 201 Created Content-Type: application/op-incoming-payment-v1+json { "id": "https://uphold.com/nicepayment/6ac630732fadd04" "accountId": "https://uphold.com/nicepayment", "state": "pending", "incomingAmount": { "amount": "10000", "assetCode": "USD", "assetScale": 2 }, "externalRef": "{\"receiveId\":\"187344\",\"sendId\":\"382076\",\"txId\":\"6c8f1bbf-af89-4ea2-b79c-8af38dfa69e8\"}" } ``` Next, NicePayment creates the outgoing payment at Alice's Payment Pointer: ```http POST /alice HTTP/1.1 Host: fynbos.me Content-Type: application/op-outgoing-payment-v1+json Accept: application/op-outgoing-payment-v1+json Signature-Input: gnap=("@request-target" "host" "authorization");created=1624564850;keyid="xyz-client" Signature: gnap=:EN/rExQ/knVi61P5AFhyMGN7aVPzk/9C7nsYAWF2RvzsoV1uNxGZklM55qCIQpuhoNty4EhiH7iwuzZBbRCQcQ==: Authorization: GNAP OS9M2PMHKUR64TB8N6BW7OZB8CDFONP219RP1LT0 { "state": "authorized", "receivingPayment": "https://uphold.com/nicepayment/6ac630732fadd04" } ``` Within Fynbos the resource server (Rafiki) checks the grant associated with the provided token and gets the following response. ```json { "active": true, "grant": "PRY5NM33OM4TB8N6BW7", "access": [ { "type": "outgoing-payment", "actions": [ "create", "authorize", ], "locations": [ "https://fynbos.me/alice" ], "limits": { "startAt": "2022-05-13T18:25:43.511Z", "expiresAt": "2023-05-13T18:25:43.511Z", "interval": "P1D", "sendAmount": { "amount": 10000, "assetCode": "USD", "assetScale": 2 } } } ], "key": { "proof": "httpsig", "jwk": { "kty": "RSA", "e": "AQAB", "kid": "xyz-1", "alg": "RS256", "n": "kOB5rR4Jv0GMeL...." } }, } ``` Rafiki, the RS, keeps balances for each time interval for every `grant`. Even though the AS confirms that this access token is valid, the RS may decline the client's request if the amount that it wants to send exceeds the limit per interval. In this case, Fynbos has a balance already associated to the grant. The balance of `PRY5NM33OM4TB8N6BW7_2022-07-17` is 40.13 USD and will go up to 140.13 USD + fees if this payment completes. This exceeds the limit associated with the grant. Fynbos declines the request: ```http HTTP/1.1 409 Conflict Content-Type: application/op-outgoing-payment-v1+json { "error": "Payment exceeds limit." } ``` In order to complete the payment, Alice needs to extend the grant it has given to NicePayment. NicePayment continues its grant request with the AS ```http POST auth/continue HTTP/1.1 Host: fynbos.dev Content-Type: application/json Authorization: GNAP YT98RY5NM33OMUKMK65U Signature-Input: sig1=... Signature: sig1=... Digest: sha256=... { "access_token": { "access": [ { "type": "outgoing-payment", "actions": [ "create", "authorize" ], "locations": [ "https://fynbos.me/alice" ], "limits": { "startAt": "2022-05-13T18:25:43.511Z", "expiresAt": "2023-05-13T18:25:43.511Z", "interval": "P1D", "sendAmount": { "amount": 10000, "assetCode": "USD", "assetScale": 2 } } }, { "type": "outgoing-payment", "actions": [ "create", "authorize" ], "locations": [ "https://fynbos.me/alice" ], "limits": { "expiresAt": "2023-06-02T18:25:43.511ZZ", "sendAmount": { "amount": 4200, "assetCode": "USD", "assetScale": 2 } } } ] }, "client": { "display": { "name": "NicePayment", "uri": "https://nicepayment.com" }, "key": { "proof": "httpsig", "jwk": { "kty": "RSA", "e": "AQAB", "kid": "xyz-1", "alg": "RS256", "n": "kOB5rR4Jv0GMeL...." } } }, "interact": { "start": [ "redirect" ], "finish": { "method": "redirect", "uri": "https://nicepayment.com/return/876FGRD8VC", "nonce": "LKLTI25DK82FX4T4QFZC" } } } ``` Fynbos sends back an instruction to NicePayment to redirect Alice to a Fynbos web page to authenticate herself and consent to the grant request. ```json { "interact": { "redirect": "https://fynbos.dev/auth/HQ4CF492MLVMSW9MKMXK", "finish": "LHMBDOFXG4Y5CVJCX821" }, "continue": { "access_token": { "value": "KU80UPRY5NM33OMUKMKS" }, "uri": "https://fynbos.dev/auth/continue" } } ``` NicePayment directs Alice to https://fynbos.dev/auth/HQ4CF492MLVMSW9MKMXK which is a web page hosted by Fynbos. Fynbos authenticates Alice and then prompts Alice to consent to giving Coil access to her account wth the updated limit of 142 USD for today. She consents. Fynbos responds to NicePayment with the following: ```json { "access_token": { "value": "T0OS9M2PMHKUR64TB8N6BW7OZB8CDFONP219RP1L", "manage": "https://fynbos.dev/auth/token/W7PRY5NM33OM4TB8N6B", "expires_in": 762534, "access": [ { "type": "outgoing-payment", "actions": [ "create", "authorize", ], "locations": [ "https://fynbos.me/alice" ], "startAt": "2022-05-13T18:25:43.511Z", "expiresAt": "2023-05-13T18:25:43.511Z", "interval": "P1D", "sendAmount": { "amount": 10000, "assetCode": "USD", "assetScale": 2 } }, { "type": "outgoing-payment", "actions": [ "create", "authorize" ], "locations": [ "https://fynbos.me/alice" ], "limits": { "expiresAt": "2023-06-02T18:25:43.511Z", "sendAmount": { "amount": 4200, "assetCode": "USD", "assetScale": 2 } } }] }, "continue": { "access_token": { "value": "5UYT98RY5NM33OMUKMK6" }, "uri": "https://fynbos.dev/auth/continue" } } ``` NicePayment now has a grant to create and authorize outgoing payments from Alice's account up to a value of 142 USD. The rest of the flow is as described in [Case 1](#Case-1-Alice-does-not-exceed-her-limit).