├── .gitignore ├── Gruntfile.js ├── LICENSE ├── README.md ├── dist ├── README.txt └── spec │ ├── payid-discovery.html │ ├── payid-discovery.txt │ ├── payid-discovery.xml │ ├── payid-easy-checkout.html │ ├── payid-easy-checkout.txt │ ├── payid-easy-checkout.xml │ ├── payid-protocol.html │ ├── payid-protocol.txt │ ├── payid-protocol.xml │ ├── payid-uri.html │ ├── payid-uri.txt │ ├── payid-uri.xml │ ├── self-sov-verifiable-payid-protocol.html │ ├── self-sov-verifiable-payid-protocol.txt │ ├── self-sov-verifiable-payid-protocol.xml │ ├── verifiable-payid-protocol.html │ ├── verifiable-payid-protocol.txt │ └── verifiable-payid-protocol.xml ├── java ├── README.md ├── pom.xml └── src │ ├── main │ └── java │ │ └── org │ │ └── payid │ │ ├── AbstractPayId.java │ │ └── PayId.java │ └── test │ ├── java │ └── org │ │ └── payid │ │ ├── PayIdInvalidValuesTest.java │ │ ├── PayIdPublicSuffixesTest.java │ │ └── PayIdValidValuesTest.java │ └── resources │ └── org │ └── payid │ └── valid-payids.json ├── package-lock.json ├── package.json └── src └── spec ├── payid-discovery.md ├── payid-easy-checkout.md ├── payid-protocol.md ├── payid-uri.md ├── self-sov-verifiable-payid-protocol.md └── verifiable-payid-protocol.md /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules/ 2 | **/.DS_Store 3 | **/.idea 4 | **/target 5 | **/*.iml 6 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | // Add new RFC proposals to this list: 3 | const rfcs = [ 4 | "src/spec/payid-uri.md", 5 | "src/spec/payid-discovery.md", 6 | "src/spec/payid-protocol.md", 7 | "src/spec/verifiable-payid-protocol.md", 8 | "src/spec/payid-easy-checkout.md", 9 | "src/spec/self-sov-verifiable-payid-protocol.md" 10 | ] 11 | 12 | // Project configuration. 13 | grunt.initConfig({ 14 | pkg: grunt.file.readJSON("package.json"), 15 | 16 | // The command that generates specs 17 | kramdown_rfc2629: { 18 | options: { 19 | outputs: ["text", "html"], 20 | outputDir: "dist/spec", 21 | removeXML: false 22 | }, 23 | your_target: { 24 | src: rfcs, 25 | } 26 | }, 27 | 28 | // The "watch" command which will watch specs for file changes, 29 | // and automatically regenerate the RFC outputs. 30 | watch: { 31 | scripts: { 32 | files: rfcs, 33 | tasks: ["kramdown_rfc2629"], 34 | options: { 35 | spawn: false 36 | } 37 | } 38 | }, 39 | 40 | }); 41 | 42 | grunt.loadNpmTasks("grunt-contrib-watch"); 43 | grunt.loadNpmTasks("grunt-kramdown-rfc2629"); 44 | }; 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2020 Ripple Labs Inc. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PayID 2 | 3 | ## Specs 4 | 5 | - [You can find the full list of RFCs here](https://github.com/payid-org/rfcs/tree/master/dist/spec) 6 | 7 | ### List of RFCs 8 | 9 | #### PayID Core 10 | - [PayID Whitepaper](https://payid.org/whitepaper.pdf) 11 | - [The 'payid' URI Scheme](https://github.com/payid-org/rfcs/blob/master/dist/spec/payid-uri.txt) 12 | - [PayID Discovery](https://github.com/payid-org/rfcs/blob/master/dist/spec/payid-discovery.txt) 13 | - [PayID Protocol](https://github.com/payid-org/rfcs/blob/master/dist/spec/payid-protocol.txt) 14 | 15 | #### Verifiable PayID 16 | - [Verifiable PayID Protocol](https://github.com/payid-org/rfcs/blob/master/dist/spec/verifiable-payid-protocol.txt) 17 | - [Self-Sovereign Verifiable PayID](https://github.com/payid-org/rfcs/blob/master/dist/spec/self-sov-verifiable-payid-protocol.txt) 18 | 19 | #### PayID Commerce 20 | - [PayID Easy Checkout](https://github.com/payid-org/rfcs/blob/master/dist/spec/payid-easy-checkout.txt) 21 | 22 | The source code for each spec is in [src/spec](https://github.com/payid-org/rfcs/tree/master/src/spec). 23 | 24 | ## Implementations 25 | 26 | Known implementations of PayID and PayID Discovery: 27 | 28 | - [TypeScript](https://github.com/payid-org/payid) 29 | - Java: TBD. 30 | - Swift: TBD. 31 | 32 | If you would like to update this list, please feel free to open a pull request against this repository. 33 | 34 | ## Generating the Spec 35 | 36 | From the root directory of the repo run: 37 | 38 | ```sh 39 | # Install IETF RFC tools 40 | gem install kramdown-rfc2629 41 | pip3 install xml2rfc 42 | 43 | # Install PayID RFC dependencies 44 | npm install 45 | 46 | # Generate the spec 47 | npm run spec 48 | ``` 49 | 50 | This generates the RFC output files in the `dist` folder using [kramdown-rfc2629](https://github.com/cabo/kramdown-rfc2629/), [xml2rfc](http://xml2rfc.ietf.org/) and [Grunt](http://gruntjs.com/) with the [Grunt kramdown_rfc2629 task](https://github.com/hildjj/grunt-kramdown-rfc2629/) 51 | 52 | To watch edits to RFC source files and auto-generate output when changes are saved run `npm run watch`. 53 | 54 | ## Authoring a new RFC 55 | 56 | First, write a Pull Request that adds the markdown file for the spec in the [src/spec](https://github.com/payid-org/rfcs/tree/master/src/spec) folder. 57 | 58 | Then, add that file to the [Gruntfile](https://github.com/payid-org/rfcs/tree/master/Gruntfile.js) list of RFCs. That way the spec output can be generated for your proposal. 59 | ## Legal 60 | 61 | **This code is not authorised for download in Australia. Any persons located in Australia are expressly prohibited from downloading, using, reproducing or distributing the code.** This code is not owned by, or associated with, NPP Australia Limited, and has no sponsorship, affiliation or other connection with the “Pay ID” service operated by NPP Australia Limited in Australia. 62 | -------------------------------------------------------------------------------- /dist/README.txt: -------------------------------------------------------------------------------- 1 | Placeholder for generated files. 2 | -------------------------------------------------------------------------------- /dist/spec/payid-discovery.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Network Working Group D. Fuelling 6 | Internet-Draft Ripple 7 | Intended status: Standards Track July 17, 2020 8 | Expires: January 18, 2021 9 | 10 | 11 | PayID Discovery 12 | draft-fuelling-payid-discovery-01 13 | 14 | Abstract 15 | 16 | This specification defines the PayID Discovery protocol, which can be 17 | used to discover information about a 'payid' URI using standard HTTP 18 | methods. 19 | 20 | The primary use-case of this protocol is to define how to transform a 21 | PayID URI into a URL that can be used with other protocols. 22 | 23 | Feedback 24 | 25 | This specification is a draft proposal, and is part of the PayID 26 | Protocol [1] initiative. Feedback related to this document should be 27 | sent in the form of a Github issue at: https://github.com/payid- 28 | org/rfcs/issues. 29 | 30 | Status of This Memo 31 | 32 | This Internet-Draft is submitted in full conformance with the 33 | provisions of BCP 78 and BCP 79. 34 | 35 | Internet-Drafts are working documents of the Internet Engineering 36 | Task Force (IETF). Note that other groups may also distribute 37 | working documents as Internet-Drafts. The list of current Internet- 38 | Drafts is at https://datatracker.ietf.org/drafts/current/. 39 | 40 | Internet-Drafts are draft documents valid for a maximum of six months 41 | and may be updated, replaced, or obsoleted by other documents at any 42 | time. It is inappropriate to use Internet-Drafts as reference 43 | material or to cite them other than as "work in progress." 44 | 45 | This Internet-Draft will expire on January 18, 2021. 46 | 47 | Copyright Notice 48 | 49 | Copyright (c) 2020 IETF Trust and the persons identified as the 50 | document authors. All rights reserved. 51 | 52 | 53 | 54 | 55 | 56 | Fuelling Expires January 18, 2021 [Page 1] 57 | 58 | Internet-Draft PayID Discovery July 2020 59 | 60 | 61 | This document is subject to BCP 78 and the IETF Trust's Legal 62 | Provisions Relating to IETF Documents 63 | (https://trustee.ietf.org/license-info) in effect on the date of 64 | publication of this document. Please review these documents 65 | carefully, as they describe your rights and restrictions with respect 66 | to this document. Code Components extracted from this document must 67 | include Simplified BSD License text as described in Section 4.e of 68 | the Trust Legal Provisions and are provided without warranty as 69 | described in the Simplified BSD License. 70 | 71 | Table of Contents 72 | 73 | 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 2 74 | 2. Terminology . . . . . . . . . . . . . . . . . . . . . . . . . 4 75 | 3. Example Usage . . . . . . . . . . . . . . . . . . . . . . . . 4 76 | 3.1. PayID Discovery by a Wallet . . . . . . . . . . . . . . . 4 77 | 3.2. PayID Discovery with Default Template . . . . . . . . . . 5 78 | 4. PayID Discovery Protocol . . . . . . . . . . . . . . . . . . 5 79 | 4.1. Interactive Mode . . . . . . . . . . . . . . . . . . . . 6 80 | 4.1.1. Step 1: Assemble PayID Discovery URL . . . . . . . . 7 81 | 4.1.2. Step 2: Query PayID Discovery URL . . . . . . . . . . 8 82 | 4.1.3. Step 3: Parse PayID Metadata . . . . . . . . . . . . 8 83 | 4.1.4. Step 4: Assemble PayID URL . . . . . . . . . . . . . 8 84 | 4.2. Fallback Mode . . . . . . . . . . . . . . . . . . . . . . 10 85 | 4.2.1. Fallback Assembly Flow . . . . . . . . . . . . . . . 10 86 | 5. PayID Discovery JRDs . . . . . . . . . . . . . . . . . . . . 10 87 | 5.1. JRD for PayID Discovery URL . . . . . . . . . . . . . . . 11 88 | 5.2. JRD for PayID URI Template . . . . . . . . . . . . . . . 11 89 | 6. Security Considerations . . . . . . . . . . . . . . . . . . . 11 90 | 6.1. Hosted PayID Discovery Services . . . . . . . . . . . . . 12 91 | 6.2. Cross-Origin Resource Sharing (CORS) . . . . . . . . . . 12 92 | 6.3. Access Control . . . . . . . . . . . . . . . . . . . . . 12 93 | 7. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 12 94 | 7.1. New Link Relation Types . . . . . . . . . . . . . . . . . 12 95 | 7.1.1. PayID Discovery URL . . . . . . . . . . . . . . . . . 12 96 | 7.1.2. PayID Discovery URI Template . . . . . . . . . . . . 12 97 | 8. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 13 98 | 9. References . . . . . . . . . . . . . . . . . . . . . . . . . 13 99 | 9.1. Normative References . . . . . . . . . . . . . . . . . . 13 100 | 9.2. Informative References . . . . . . . . . . . . . . . . . 14 101 | 9.3. URIs . . . . . . . . . . . . . . . . . . . . . . . . . . 14 102 | Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 14 103 | 104 | 1. Introduction 105 | 106 | PayID Discovery is used to transform a PayID URI [PAYID-URI] into a 107 | URL (defined below as a PayID Discovery URL) that can then be used by 108 | 109 | 110 | 111 | 112 | Fuelling Expires January 18, 2021 [Page 2] 113 | 114 | Internet-Draft PayID Discovery July 2020 115 | 116 | 117 | higher-order protocols to discover metadata about a PayID-enabled 118 | service provider. 119 | 120 | This document specifies two modes of PayID discovery: one using 121 | Webfinger [RFC7033] to resolve a corresponding PayID Discovery URL 122 | from a PayID using an interactive protocol. The second mode uses a 123 | manual mechanism to assemble a PayID Discovery URL from a PayID by- 124 | hand. 125 | 126 | In 'interactive' mode, a PayID can be presented to a Webfinger- 127 | enabled service endpoint that supports PayID Discovery. The resource 128 | returns a Webfinger-compliant JavaScript Object Notation (JSON) 129 | [RFC4627] object that can be used to perform PayID Discovery as 130 | defined in section 4.1 of this document. 131 | 132 | As an alternative, "manual" mode MAY be used to decompose a PayID 133 | into a URL, without any intermediate server interaction by simply 134 | transposing portions of a PayID URI into a URL format. This 135 | procedure is defined in section 4.2 of this document. 136 | 137 | It should be noted that "manual" mode does not allow divergence 138 | between the string characters in a PayID URI and any corresponding 139 | PayID URL. Interactive mode, on the other hand, does allow such 140 | divergence, and is thus more powerful. For example, in manual mode, 141 | the PayID 'alice$example.com' MUST always map to the URL 142 | 'https://example.com/alice', whereas in interactive mode that same 143 | PayID URI can map to any arbitrary URL structure determined by the 144 | service provider, such as 'https://example.com/users/alice'. 145 | 146 | Information returned via PayID Discovery might be used for direct 147 | human consumption (e.g., looking up someone's Bitcoin address), or it 148 | might be used by systems to help carry out some operation (e.g., 149 | facilitating, with additional security mechanisms, protocols to 150 | support compliance or other legal requirements necessary to 151 | facilitate a payment). 152 | 153 | The information returned via this protocol is intended to be static 154 | in nature. As such, PayID Discovery is not intended to be used to 155 | return dynamic information like a payment account balance or the 156 | current status of a payment account. 157 | 158 | PayID Discovery is designed to be used across many applications. Use 159 | of PayID Discovery is illustrated in the examples in Section 3 and 160 | described more formally in Section 4. 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | Fuelling Expires January 18, 2021 [Page 3] 169 | 170 | Internet-Draft PayID Discovery July 2020 171 | 172 | 173 | 2. Terminology 174 | 175 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 176 | "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and 177 | "OPTIONAL" in this document are to be interpreted as described in 178 | [RFC2119]. 179 | 180 | 3. Example Usage 181 | 182 | This section shows sample uses of PayID Discovery in several 183 | hypothetical scenarios. 184 | 185 | 3.1. PayID Discovery by a Wallet 186 | 187 | Imagine Alice wishes to send a friend some XRP from a web-based 188 | wallet provider that Alice has an account on. Alice would log-in to 189 | the wallet provider and enter Bob's PayID (say, 190 | "bob$receiver.example.com") into the wallet UI to start the payment. 191 | 192 | The Wallet application would first perform a WebFinger query looking 193 | for the PayID Discovery service provider, like this: 194 | 195 | GET /.well-known/webfinger? 196 | resource=payid%3Abob%24receiver.example.com 197 | HTTP/1.1 198 | Host: receiver.example.com 199 | 200 | The server might respond like this: 201 | 202 | HTTP/1.1 200 OK 203 | Access-Control-Allow-Origin: * 204 | Content-Type: application/jrd+json 205 | 206 | { 207 | "subject" : "payid:bob$receiver.example.com", 208 | "links" : 209 | [ 210 | { 211 | "rel": "https://payid.org/ns/payid-uri-template/1.0", 212 | "template": "https://receiver.example.com/users/{acctpart}" 213 | } 214 | ] 215 | } 216 | 217 | Alice's wallet then uses the URL template found in the "template" 218 | property to assemble the specified PayId URL, 219 | "https://receiver.example.com/users/bob". 220 | 221 | 222 | 223 | 224 | Fuelling Expires January 18, 2021 [Page 4] 225 | 226 | Internet-Draft PayID Discovery July 2020 227 | 228 | 229 | Per [RFC7033], Webfinger requests can be filtered by using a "rel" 230 | parameter in the Webfinger request. Because support for the "rel" 231 | parameter is not required nor guaranteed, the client must not assume 232 | the "links" array will contain only the link relations related to 233 | PayID Discovery. 234 | 235 | 3.2. PayID Discovery with Default Template 236 | 237 | Imagine Alice, as in the example above, wants to send a friend some 238 | XRP from a web-based wallet provider that Alice has an account on. 239 | However, in this example, let's assume that the PayID Alice is 240 | wanting to pay doesn't support "interactive" PayID discovery (i.e., 241 | the receiver's server doesn't support Webfinger). 242 | 243 | Alice would log-in to her wallet provider and enter Bob's PayID (say 244 | "bob$receiver.example.com") to make a payment. 245 | 246 | The Wallet application would first attempt a WebFinger query as in 247 | the example above, like this: 248 | 249 | GET /.well-known/webfinger? 250 | resource=payid%3Abob%24receiver.example.com& 251 | HTTP/1.1 252 | Host: receiver.example.com 253 | 254 | However, in this case the "receiver.example.com" server doesn't 255 | support "interactive" PayID Discovery, so the server responds like 256 | this: 257 | 258 | HTTP/1.1 404 NOT FOUND 259 | 260 | Because Alice's Wallet can utilize "manual" PayID Discovery, the 261 | wallet software merely transforms "bob$receiever.example.com" into 262 | the URL "https://receiver.example.com/bob". Alice's wallet then uses 263 | that URL to continue making a PayID payment. 264 | 265 | It should be noted that "manual" mode does not allow the PayID URI to 266 | diverge from the underlying URL returned via PayID Discovery. 267 | Because of this, "interactive" PayID Discovery is generally 268 | preferred. 269 | 270 | 4. PayID Discovery Protocol 271 | 272 | The PayID Discovery protocol is used to request information about an 273 | entity identified by a PayID URI. 274 | 275 | When successful, PayID Discovery always yields a PayID URL, which is 276 | a URI as defined by [RFC3986] using the 'https' scheme defined in 277 | 278 | 279 | 280 | Fuelling Expires January 18, 2021 [Page 5] 281 | 282 | Internet-Draft PayID Discovery July 2020 283 | 284 | 285 | section 2.7.2 [RFC7230]. A PayID URL can be used for any purposes 286 | outside the scope of this document. 287 | 288 | PayID Discovery is performed using one of two modes: "interactive" or 289 | "manual." Clients MUST attempt "interactive" mode first. If that 290 | mode fails to yield a PayID URL, then "manual" mode MAY be used as an 291 | alternative discovery mechanism. 292 | 293 | 4.1. Interactive Mode 294 | 295 | Interactive PayID Discovery is broken up into a series of steps, each 296 | of which is defined in more detail below. The following is a visual 297 | representation of the protocol flow: 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | Fuelling Expires January 18, 2021 [Page 6] 337 | 338 | Internet-Draft PayID Discovery July 2020 339 | 340 | 341 | +--------------------------+ 342 | | PayID URI | 343 | | alice$example.com | 344 | +--------------------------+ 345 | | 346 | v 347 | +--------------------------+ 348 | | Assemble | 349 | | PayID Discovery URL | 350 | +--------------------------+ 351 | | 352 | v 353 | +--------------------------+ 354 | | Query | 355 | +-----| PayID Discovery URL |-----+ 356 | | +--------------------------+ | 357 | | Success 358 | | | 359 | | v 360 | | +---------------------------+ 361 | Failure | Parse PayID Metadata | 362 | | +---------------------------+ 363 | | | 364 | | v 365 | | +---------------------------+ 366 | | | Select PayID URI Template | 367 | | +---------------------------+ 368 | | | 369 | v v 370 | +--------------------------+ +---------------------------+ 371 | | Manual PayID Discovery | | Assemble PayID URL | 372 | +--------------------------+ +---------------------------+ 373 | | | 374 | +---------------------+----------------+ 375 | | 376 | v 377 | +---------------------------+ 378 | | PayID URL | 379 | | https://example.com/alice | 380 | +---------------------------+ 381 | 382 | 4.1.1. Step 1: Assemble PayID Discovery URL 383 | 384 | PayID Discovery utilizes the Webfinger [RFC7033] specification in a 385 | narrowly defined profile. 386 | 387 | 388 | 389 | 390 | 391 | 392 | Fuelling Expires January 18, 2021 [Page 7] 393 | 394 | Internet-Draft PayID Discovery July 2020 395 | 396 | 397 | This document defines a PayID Discovery URL as being a Webfinger 398 | resource URI where the specified resource value is a valid PayID URI 399 | [PAYID-URI]. 400 | 401 | For example, the PayID Discovery URL for alice$example.com is 402 | 403 | https://example.com/.well-known/webfinger? 404 | resource=payid%3Aalice%24example.com 405 | 406 | 4.1.2. Step 2: Query PayID Discovery URL 407 | 408 | A Webfinger query MUST be performed against the PayID Discovery URL, 409 | as described in section 4.2 of Webfinger. 410 | 411 | In response, the WebFinger resource returns a JSON Resource 412 | Descriptor (JRD) as the resource representation to convey information 413 | about the requested PayID. 414 | 415 | If the Webfinger endpoint returns a non-200 HTTP response status 416 | code, then interactive PayID Discovery is considered to have failed. 417 | Clients MAY attempt to assemble a PayID URL using "manual" mode as 418 | defined in section 4.2.1 of this document. 419 | 420 | 4.1.3. Step 3: Parse PayID Metadata 421 | 422 | If the PayID Discovery server returns a valid response, the response 423 | will contain one or more of the JRDs defined in section 5 of this 424 | document. 425 | 426 | If any of the JRDs contain a 'rel' value that represents a PayID URL 427 | Template, then that template value MUST be used in the next protocol 428 | step. 429 | 430 | Failing the above, if the 'rel' value of any JRDs represents a PayID 431 | Discovery URL, then that URL MUST be used in step 2 above, repeated 432 | recursively if needed, until a valid PayID URI Template is obtained. 433 | That URI Template value MUST be used in the next protocol step. 434 | 435 | 4.1.4. Step 4: Assemble PayID URL 436 | 437 | A PayID URL is constructed by applying the PayID URI to the PayID URI 438 | Template string obtained in the step above. The PayID URI template 439 | MAY contain a URI string without any variables to represent a host- 440 | level PayID URL that is identical for every PayID URI on a particular 441 | host. 442 | 443 | For example, a PayID Discovery endpoint that only supports a single 444 | account might use a URI template string with no variables, like this: 445 | 446 | 447 | 448 | Fuelling Expires January 18, 2021 [Page 8] 449 | 450 | Internet-Draft PayID Discovery July 2020 451 | 452 | 453 | { 454 | "rel": "https://payid.org/ns/payid-uri-template/1.0", 455 | "template": "https://example.com/alice" 456 | } 457 | 458 | The result of this step is the PayID URL. Once obtained, PayID 459 | Discovery is considered to have completed successfully. 460 | 461 | 4.1.4.1. Template Syntax 462 | 463 | This specification defines a simple template syntax for PayID URI 464 | transformation. A template is a string containing brace-enclosed 465 | ("{}") variable names marking the parts of the string that are to be 466 | substituted by the corresponding variable values. 467 | 468 | This specification defines a one variable - "acctpart" - which 469 | corresponds to the 'acctpart' of a PayID URI as defined in 470 | [PAYID-URI]. 471 | 472 | When substituting the 'acctpart' value into a URI 'path' as defined 473 | by [RFC3986], values MUST NOT be percent or otherwise encoded because 474 | the 'acctpart' value of a PayID URI always conforms to the character 475 | set allowed by paths in [RFC3986]. 476 | 477 | However, before substituting template variables into a URI 'query' 478 | part, values MUST be encoded using UTF-8, and any character other 479 | than unreserved (as defined by [RFC3986]) MUST be percent-encoded per 480 | [RFC3986]. 481 | 482 | Protocols MAY define additional variables and syntax rules, but MUST 483 | NOT change the meaning of the 'acctpart' variable. If a client is 484 | unable to successfully process a template (e.g., unknown variable 485 | names, unknown or incompatible syntax), the JRD SHOULD be ignored. 486 | 487 | The template syntax ABNF is as follows: 488 | 489 | uri-char = ( reserved / unreserved / pct-encoded ) 490 | var-char = ALPHA / DIGIT / "." / "_" 491 | var-name = %x61.63.63.74.70.61.72.74 / ( 1*var-char ) ; "acctpart" or 492 | other names 493 | variable = "{" var-name "}" 494 | PAYID-URI-Template = *( uri-char / variable ) 495 | 496 | For example: 497 | 498 | Input: alice$example.org 499 | Template: https://example.org/{acctpart} 500 | Output: https://example.org/alice 501 | 502 | 503 | 504 | Fuelling Expires January 18, 2021 [Page 9] 505 | 506 | Internet-Draft PayID Discovery July 2020 507 | 508 | 509 | 4.2. Fallback Mode 510 | 511 | If "Interactive" mode is not supported or otherwise fails to yield a 512 | PayID URL, then a PayID URL MAY be assembled manually using the 513 | following predefined ruleset: 514 | 515 | 1. Decompose the PayID URI into its component parts, per 516 | [PAYID-URI], capturing the 'acctpart' and 'host' values. 517 | 518 | 2. Using the 'acctpart' and 'host', assemble a URL by substituting 519 | each value into the following string using no special encoding or 520 | other character adjustments: "https://{host}/{acctpart}". 521 | 522 | For example: 523 | 524 | Input: bob.primary$example.org 525 | Output: https://example.org/bob.primary 526 | 527 | The resulting URL is a PayID URL. 528 | 529 | 4.2.1. Fallback Assembly Flow 530 | 531 | The following is a visual representation of the Fallback Assembly 532 | protocol flow: 533 | 534 | +--------------------------+ 535 | | PayID URI | 536 | | alice$example.com | 537 | +--------------------------+ 538 | | 539 | v 540 | +--------------------------+ 541 | |Manual PayID URL Assembly | 542 | +--------------------------+ 543 | | 544 | v 545 | +---------------------------+ 546 | | PayID URL | 547 | | https://example.com/alice | 548 | +---------------------------+ 549 | 550 | 5. PayID Discovery JRDs 551 | 552 | This document defines two JRDs that conform to section 4.4 of the 553 | Webfinger RFC. 554 | 555 | 556 | 557 | 558 | 559 | 560 | Fuelling Expires January 18, 2021 [Page 10] 561 | 562 | Internet-Draft PayID Discovery July 2020 563 | 564 | 565 | 5.1. JRD for PayID Discovery URL 566 | 567 | This type of JRD can be used to represent a URL that is a PayID 568 | Discovery URL. This is useful for delegating PayID Discovery to 569 | another service endpoint: 570 | 571 | o 'rel': "https://payid.org/ns/payid-discovery-url/1.0" 572 | 573 | o 'href': A PayID Discovery URL that clients can dereference to 574 | perform interactive PayID Discovery. 575 | 576 | The following is an example of a JRD that indicates a PayID Discovery 577 | URL: 578 | 579 | { 580 | "rel": "https://payid.org/ns/payid-discovery-url/1.0", 581 | "href": "https://delegate.example.com/.well-known/webfinger?resource= 582 | payid%3Aalice%24example.com" 583 | } 584 | 585 | 5.2. JRD for PayID URI Template 586 | 587 | This type of JRD can be used to represent a URL that is a PayID URL 588 | Template. 589 | 590 | o 'rel': "https://payid.org/ns/payid-uri-template/1.0" 591 | 592 | o 'template': A PayID URI Template 593 | 594 | The following is an example of a JRD that indicates a PayID URI 595 | Template: 596 | 597 | { 598 | "rel": "https://payid.org/ns/payid-uri-template/1.0", 599 | "template": "https://example.com/{acctpart}" 600 | } 601 | 602 | 6. Security Considerations 603 | 604 | Various security considerations should be taken into account for 605 | PayID Discovery. 606 | 607 | Among other resource, consult section 9 of [RFC7033] and section 7 of 608 | [RFC3986] for important security considerations involved in PayID 609 | Discovery. 610 | 611 | 612 | 613 | 614 | 615 | 616 | Fuelling Expires January 18, 2021 [Page 11] 617 | 618 | Internet-Draft PayID Discovery July 2020 619 | 620 | 621 | 6.1. Hosted PayID Discovery Services 622 | 623 | As with most services provided on the Internet, it is possible for a 624 | domain owner to utilize "hosted" WebFinger services. Consult section 625 | 7 of [RFC7033] for considerations that could apply to both "manual" 626 | and "interactive" PayID Discovery when hosted by a third-party. 627 | 628 | 6.2. Cross-Origin Resource Sharing (CORS) 629 | 630 | PayID Discovery resources might not be accessible from a web browser 631 | due to "Same-Origin" policies. See section 5 of [RFC7033] for CORS 632 | considerations that apply to both "manual" and "interactive" PayID 633 | Discovery modes. 634 | 635 | 6.3. Access Control 636 | 637 | As with all web resources, access to the PayID Discovery resource 638 | could require authentication. See section 6 of [RFC7033] for Access 639 | Control considerations that could apply to both "manual" and 640 | "interactive" PayID Discovery modes. 641 | 642 | 7. IANA Considerations 643 | 644 | 7.1. New Link Relation Types 645 | 646 | This document defines the following Link relation types per 647 | [RFC7033]. See section 3 for examples of each type of Link. 648 | 649 | 7.1.1. PayID Discovery URL 650 | 651 | o Relation Type ('rel'): "https://payid.org/ns/payid-discovery- 652 | url/1.0" 653 | 654 | o Media Type: "application/jrd+json" 655 | 656 | o Description: PayID Discovery URL, version 1.0 657 | 658 | 7.1.2. PayID Discovery URI Template 659 | 660 | o Relation Type ('rel'): "https://payid.org/ns/payid-uri- 661 | template/1.0" 662 | 663 | o Media Type: "application/jrd+json" 664 | 665 | o Description: PayID Discovery URI Template, version 1.0 666 | 667 | 668 | 669 | 670 | 671 | 672 | Fuelling Expires January 18, 2021 [Page 12] 673 | 674 | Internet-Draft PayID Discovery July 2020 675 | 676 | 677 | 8. Acknowledgments 678 | 679 | This document was heavily influenced by, and builds upon, Webfinger 680 | [RFC7033] (adapted for a payments use-case) as well as the supporting 681 | RFCs that it relies upon and that influenced it, especially [RFC5988] 682 | and [RFC6415]. The author would like to acknowledge the 683 | contributions of everyone who worked on those and any related 684 | specifications. 685 | 686 | In addition, the author would like to acknowledge everyone who 687 | provided feedback and use-cases for this derivative specification. 688 | 689 | 9. References 690 | 691 | 9.1. Normative References 692 | 693 | [PAYID-URI] 694 | Fuelling, D., "The 'payid' URI Scheme", n.d., 695 | . 696 | 697 | [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate 698 | Requirement Levels", BCP 14, RFC 2119, 699 | DOI 10.17487/RFC2119, March 1997, 700 | . 701 | 702 | [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform 703 | Resource Identifier (URI): Generic Syntax", STD 66, 704 | RFC 3986, DOI 10.17487/RFC3986, January 2005, 705 | . 706 | 707 | [RFC4627] Crockford, D., "The application/json Media Type for 708 | JavaScript Object Notation (JSON)", RFC 4627, 709 | DOI 10.17487/RFC4627, July 2006, 710 | . 711 | 712 | [RFC6570] Gregorio, J., Fielding, R., Hadley, M., Nottingham, M., 713 | and D. Orchard, "URI Template", RFC 6570, 714 | DOI 10.17487/RFC6570, March 2012, 715 | . 716 | 717 | [RFC7033] Jones, P., Salgueiro, G., Jones, M., and J. Smarr, 718 | "WebFinger", RFC 7033, DOI 10.17487/RFC7033, September 719 | 2013, . 720 | 721 | [RFC7230] Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer 722 | Protocol (HTTP/1.1): Message Syntax and Routing", 723 | RFC 7230, DOI 10.17487/RFC7230, June 2014, 724 | . 725 | 726 | 727 | 728 | Fuelling Expires January 18, 2021 [Page 13] 729 | 730 | Internet-Draft PayID Discovery July 2020 731 | 732 | 733 | 9.2. Informative References 734 | 735 | [RFC5988] Nottingham, M., "Web Linking", RFC 5988, 736 | DOI 10.17487/RFC5988, October 2010, 737 | . 738 | 739 | [RFC6415] Hammer-Lahav, E., Ed. and B. Cook, "Web Host Metadata", 740 | RFC 6415, DOI 10.17487/RFC6415, October 2011, 741 | . 742 | 743 | 9.3. URIs 744 | 745 | [1] https://payid.org/ 746 | 747 | Author's Address 748 | 749 | David Fuelling 750 | Ripple 751 | 315 Montgomery Street 752 | San Francisco, CA 94104 753 | US 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | Fuelling Expires January 18, 2021 [Page 14] 785 | -------------------------------------------------------------------------------- /dist/spec/payid-easy-checkout.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Network Working Group N. Kramer 6 | Internet-Draft D. Fuelling 7 | Intended status: Standards Track I. Simpson 8 | Expires: February 7, 2021 Ripple 9 | August 06, 2020 10 | 11 | 12 | Draft 1 - PayID Easy Checkout Protocol 13 | payid-easy-checkout-protocol 14 | 15 | Abstract 16 | 17 | This specification formalizes how a payment recipient, such as a 18 | merchant or a non-profit, can automatically initiate a payment from a 19 | sender using only the sender's PayID. 20 | 21 | Feedback 22 | 23 | This specification is a draft proposal, and is part of the PayID 24 | Protocol [1] initiative. Feedback related to this document should be 25 | sent in the form of a Github issue at: https://github.com/payid- 26 | org/rfcs/issues. 27 | 28 | Status of This Memo 29 | 30 | This Internet-Draft is submitted in full conformance with the 31 | provisions of BCP 78 and BCP 79. 32 | 33 | Internet-Drafts are working documents of the Internet Engineering 34 | Task Force (IETF). Note that other groups may also distribute 35 | working documents as Internet-Drafts. The list of current Internet- 36 | Drafts is at https://datatracker.ietf.org/drafts/current/. 37 | 38 | Internet-Drafts are draft documents valid for a maximum of six months 39 | and may be updated, replaced, or obsoleted by other documents at any 40 | time. It is inappropriate to use Internet-Drafts as reference 41 | material or to cite them other than as "work in progress." 42 | 43 | This Internet-Draft will expire on February 7, 2021. 44 | 45 | Copyright Notice 46 | 47 | Copyright (c) 2020 IETF Trust and the persons identified as the 48 | document authors. All rights reserved. 49 | 50 | This document is subject to BCP 78 and the IETF Trust's Legal 51 | Provisions Relating to IETF Documents 52 | (https://trustee.ietf.org/license-info) in effect on the date of 53 | 54 | 55 | 56 | Kramer, et al. Expires February 7, 2021 [Page 1] 57 | 58 | Internet-Draft Draft 1 - PayID Easy Checkout Protocol August 2020 59 | 60 | 61 | publication of this document. Please review these documents 62 | carefully, as they describe your rights and restrictions with respect 63 | to this document. Code Components extracted from this document must 64 | include Simplified BSD License text as described in Section 4.e of 65 | the Trust Legal Provisions and are provided without warranty as 66 | described in the Simplified BSD License. 67 | 68 | Table of Contents 69 | 70 | 1. Terminology . . . . . . . . . . . . . . . . . . . . . . . . . 2 71 | 2. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3 72 | 3. PayID Easy Checkout Protocol . . . . . . . . . . . . . . . . 3 73 | 3.1. PayID Easy Checkout Discovery . . . . . . . . . . . . . . 4 74 | 3.1.1. Step 1: Assemble PayID Easy Checkout Discovery URL . 4 75 | 3.1.2. Step 2: Query PayID Easy Checkout Discovery URL . . . 4 76 | 3.1.3. Step 3: Parse PayID Easy Checkout Metadata . . . . . 4 77 | 3.2. PayID Easy Checkout URL Assembly . . . . . . . . . . . . 5 78 | 3.2.1. PayID Easy Checkout URL Query Parameters . . . . . . 5 79 | 4. PayID Easy Checkout JRDs . . . . . . . . . . . . . . . . . . 6 80 | 5. Security Considerations . . . . . . . . . . . . . . . . . . . 7 81 | 5.1. PayID Easy Checkout Redirection URI Manipulation . . . . 7 82 | 5.2. Access Control . . . . . . . . . . . . . . . . . . . . . 7 83 | 6. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 8 84 | 7. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 8 85 | 8. Appendix . . . . . . . . . . . . . . . . . . . . . . . . . . 8 86 | 8.1. Motivation . . . . . . . . . . . . . . . . . . . . . . . 8 87 | 8.1.1. Design Goals . . . . . . . . . . . . . . . . . . . . 8 88 | 8.2. Example Usage . . . . . . . . . . . . . . . . . . . . . . 9 89 | 8.2.1. PayID Easy Checkout Initiation . . . . . . . . . . . 9 90 | 8.2.2. PayID Easy Checkout Wallet Discovery . . . . . . . . 9 91 | 8.2.3. Assemble PayID Easy Checkout URL with Query 92 | Parameters . . . . . . . . . . . . . . . . . . . . . 10 93 | 8.2.4. Redirect Sender to Their Wallet . . . . . . . . . . . 11 94 | 8.2.5. Sender Confirms Payment . . . . . . . . . . . . . . . 11 95 | 9. References . . . . . . . . . . . . . . . . . . . . . . . . . 11 96 | 9.1. Normative References . . . . . . . . . . . . . . . . . . 11 97 | 9.2. Informative References . . . . . . . . . . . . . . . . . 12 98 | 9.3. URIs . . . . . . . . . . . . . . . . . . . . . . . . . . 12 99 | Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 13 100 | 101 | 1. Terminology 102 | 103 | This protocol can be referred to as the "PayID Easy Checkout 104 | Protocol". It uses the following terminology: 105 | 106 | o PayID Easy Checkout Client: A client that assembles a PayID Easy 107 | Checkout URL using information obtained from PayID Discovery 108 | Server via [PAYID-DISCOVERY]. 109 | 110 | 111 | 112 | Kramer, et al. Expires February 7, 2021 [Page 2] 113 | 114 | Internet-Draft Draft 1 - PayID Easy Checkout Protocol August 2020 115 | 116 | 117 | o PayID Discovery Server: An endpoint that returns a PayID Discovery 118 | JRD conforming to [PAYID-DISCOVERY]. 119 | 120 | o Recipient: An individual or entity receiving a payment (e.g., 121 | e-commerce merchant, charity). 122 | 123 | o Sender: An individual or entity originating a payment to a 124 | "recipient". 125 | 126 | o Wallet: A device or application that holds funds (may be a non- 127 | custodial wallet). 128 | 129 | o PayID Easy Checkout URL: A URL that is the result of this 130 | protocol; can be used to redirect a client to a wallet 131 | corresponding to a particular PayID as defined in [PAYID-URI]. 132 | 133 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 134 | "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and 135 | "OPTIONAL" in this document are to be interpreted as described in 136 | [RFC2119] and [RFC9174][]. 137 | 138 | 2. Introduction 139 | 140 | The PayID Easy Checkout Protocol allows a recipient (e.g., an online 141 | merchant or a charity) to request a payment from a sender using only 142 | the sender's PayID [PAYID-URI]. Implementations of this protocol 143 | require little to no server-side engineering effort, while creating a 144 | seamless and uniform user experience for senders. 145 | 146 | The main focus of the protocol is on PayID Easy Checkout Discovery, 147 | which defines how a PayID Easy Checkout Client can retrieve a PayID 148 | Easy Checkout URL and use it to initiate a payment to the merchant. 149 | 150 | Though the Section 8 of this specification provides an example usage 151 | of this protocol using Web Redirects, supplemental RFCs are needed to 152 | define any different ways in which a PayID client can utilize a PayID 153 | Easy Checkout URL. 154 | 155 | 3. PayID Easy Checkout Protocol 156 | 157 | The PayID Easy Checkout Protocol can be used to initiate an end-to- 158 | end checkout flow between a payment recipient, such as an online 159 | merchant, and a sender. 160 | 161 | The protocol is comprised of two parts: 162 | 163 | 1. PayID Easy Checkout Discovery 164 | 165 | 166 | 167 | 168 | Kramer, et al. Expires February 7, 2021 [Page 3] 169 | 170 | Internet-Draft Draft 1 - PayID Easy Checkout Protocol August 2020 171 | 172 | 173 | 2. PayID Easy Checkout URL Assembly 174 | 175 | 3.1. PayID Easy Checkout Discovery 176 | 177 | PayID Easy Checkout Discovery extends [PAYID-DISCOVERY] by defining a 178 | new link relation in the PayID metadata JRD returned by a PayID 179 | Discovery query. This link relation, defined in the JRD (Section 4) 180 | section of this specification, includes the URL on the sender's 181 | wallet that can be used to initiate a payment. 182 | 183 | Recipients who wish to initiate an Easy Checkout flow MUST first 184 | query the sender's PayID Discovery Server to obtain a PayID Easy 185 | Checkout URL. Therefore, PayID Discovery Servers that wish to enable 186 | PayID Easy Checkout MUST include an Easy Checkout JRD Link in all 187 | PayID Easy Checkout Discovery responses. 188 | 189 | Recipients SHOULD implement fallback measures to complete a checkout 190 | flow if a sender's wallet does not support PayID Easy Checkout. 191 | 192 | The following steps describe how a PayID Easy Checkout Client can 193 | query a PayID Discovery Server to obtain a PayID Easy Checkout URL. 194 | 195 | 3.1.1. Step 1: Assemble PayID Easy Checkout Discovery URL 196 | 197 | The process of assembling a PayID Discovery URL is defined in section 198 | 4.1.1 of [PAYID-DISCOVERY]. 199 | 200 | 3.1.2. Step 2: Query PayID Easy Checkout Discovery URL 201 | 202 | The process of querying the PayID Discovery URL is defined in section 203 | 4.1.2 of [PAYID-DISCOVERY]. 204 | 205 | Clients SHOULD implement fallback measures to complete checkout if 206 | the PayID Easy Checkout Discovery query fails. 207 | 208 | 3.1.3. Step 3: Parse PayID Easy Checkout Metadata 209 | 210 | If PayID Easy Checkout is supported, a PayID Discovery server MUST 211 | respond to discovery requests with an HTTP status code of "200" and a 212 | JSON payload containing a JRD with an Easy Checkout link relation. 213 | 214 | For example, a PayID Discovery Server might respond to a PayID 215 | Discovery query with the following payload: 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | Kramer, et al. Expires February 7, 2021 [Page 4] 225 | 226 | Internet-Draft Draft 1 - PayID Easy Checkout Protocol August 2020 227 | 228 | 229 | { 230 | "subject": "payid:alice$wallet.com", 231 | "links": [ 232 | { 233 | "rel" : "https://payid.org/ns/payid-easy-checkout-uri/1.0", 234 | "href": "https://wallet.com/checkout" 235 | } 236 | ] 237 | } 238 | 239 | A PayID Easy Checkout client MUST parse this response to find the 240 | PayID Easy Checkout Link. If the JRD returned from the PayID 241 | Discovery query does not contain a PayID Easy Checkout Link in its 242 | 'links' collection, PayID Easy Checkout is considered to have failed. 243 | 244 | However, if a PayID Easy Checkout URL can been obtained from the 245 | PayID Easy Checkout Link, PayID Easy Checkout Discovery is considered 246 | to be complete. 247 | 248 | 3.2. PayID Easy Checkout URL Assembly 249 | 250 | A PayID Easy Checkout URL represents the resource on a wallet that 251 | can be used by a sender to complete a payment. However, before 252 | directing a sender to their wallet, the recipient MUST append all of 253 | the query parameters defined in the following section 254 | (Section 3.2.1). 255 | 256 | Once a PayID Easy Checkout URL is assembled, PayID Easy Checkout is 257 | considered to be complete. 258 | 259 | 3.2.1. PayID Easy Checkout URL Query Parameters 260 | 261 | This specification defines several query parameter names and 262 | corresponding datatypes which MUST be added to the PayID Easy 263 | Checkout URL before redirecting a sender to their wallet. The PayID 264 | Easy Checkout URL SHOULD be parsed by the wallet in order to retrieve 265 | any values set by the recipient. It is RECOMMENDED that wallets use 266 | these values to pre-populate a payment transaction. 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | Kramer, et al. Expires February 7, 2021 [Page 5] 281 | 282 | Internet-Draft Draft 1 - PayID Easy Checkout Protocol August 2020 283 | 284 | 285 | +----------------+---------+----------------------------------------+ 286 | | Name | Type | Description | 287 | +----------------+---------+----------------------------------------+ 288 | | amount | integer | The amount that should be sent by the | 289 | | | | sender to the recipient. | 290 | | | | | 291 | | receiverPayId | string | The [PAYID-URI] of the receiver. | 292 | | | | | 293 | | assetCode | string | The currency code that denominates the | 294 | | | | amount as defined in [PAYID-PROTOCOL]. | 295 | | | | | 296 | | assetScale | short | Defines how many units make up one | 297 | | | | regular unit of the assetCode. | 298 | | | | | 299 | | paymentNetwork | string | The payment network, as defined in | 300 | | | | [PAYID-PROTOCOL], that the sender | 301 | | | | should use to send a payment. | 302 | | | | | 303 | | nextUrl | HTTP | A URL that the sender's wallet can | 304 | | | Url | navigate a sender to after the sender | 305 | | | string | completes a payment. | 306 | +----------------+---------+----------------------------------------+ 307 | 308 | When adding values into a URI 'query' part as defined by [RFC3986], 309 | values with characters outside the character set allowed by query 310 | parameters in [RFC3986] MUST be percent or otherwise encoded. 311 | 312 | Protocols MAY define additional query parameter names and syntax 313 | rules, but MUST NOT change the meaning of the variables specified in 314 | this document. 315 | 316 | For example: 317 | 318 | Input: alice$wallet.com 319 | amount=10 320 | receiverPayId=pay$merchant.com 321 | assetCode=XRP 322 | assetScale=6 323 | network=XRPL 324 | nextUrl=https://merchant.com/thankyou 325 | PayID Easy Checkout URL: https://wallet.com/checkout 326 | Output: https://wallet.com/checkout?amount=100000&receiverPayId=payid%2Apay%24merchant.com&assetCode=XRP&assetScale=6&paymentNetwork=XRPL&nextUrl=https%3A%2F%2Fmerchant.com%2Fthankyou 327 | 328 | 4. PayID Easy Checkout JRDs 329 | 330 | This section defines the PayID Easy Checkout Link Relation, which 331 | conforms to section 4.4 of Webfinger [RFC7033]. 332 | 333 | 334 | 335 | 336 | Kramer, et al. Expires February 7, 2021 [Page 6] 337 | 338 | Internet-Draft Draft 1 - PayID Easy Checkout Protocol August 2020 339 | 340 | 341 | The Link MUST include the Link Relation Type defined in PayID Easy 342 | Checkout URL (Section 6) in the object's 'rel' field. The Link MUST 343 | also include a PayID Easy Checkout URL in the 'href' field of the 344 | link. 345 | 346 | * 'rel': `https://payid.org/ns/payid-easy-checkout-uri/1.0` 347 | * 'href': {A PayID Easy Checkout URL} 348 | 349 | The following is an example of a PayID Easy Checkout Link: 350 | 351 | { 352 | "rel": "https://payid.org/ns/payid-easy-checkout-uri/1.0", 353 | "href": "https://wallet.com/checkout" 354 | } 355 | 356 | 5. Security Considerations 357 | 358 | Various security considerations should be taken into account for 359 | PayID Easy Checkout. 360 | 361 | The security considerations for PayID Easy Checkout Discovery are 362 | discussed in section 6 of [PAYID-DISCOVERY]. 363 | 364 | 5.1. PayID Easy Checkout Redirection URI Manipulation 365 | 366 | When a sender uses the resource located at the PayID Easy Checkout 367 | URL, an attacker could manipulate the data encoded in the URL to 368 | trick the sender into sending a payment to a different PayID than was 369 | originally requested, or manipulate other parts of PayID Easy 370 | Checkout data to trick the sender. 371 | 372 | Additionally, if an attacker gains access to the merchant 373 | application, an attacker could replace the PayID Easy Checkout URL to 374 | execute a phishing or other attack. 375 | 376 | 5.2. Access Control 377 | 378 | As with all web resources, access to the PayID Discovery resource 379 | could require authentication. See section 6 of [RFC7033] for Access 380 | Control considerations. 381 | 382 | Furthermore, it is RECOMMENDED that PayID Discovery Servers only 383 | expose PayID Easy Checkout URLs which resolve to a protected resource 384 | (e.g., by logging into a wallet) before allowing access. 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | Kramer, et al. Expires February 7, 2021 [Page 7] 393 | 394 | Internet-Draft Draft 1 - PayID Easy Checkout Protocol August 2020 395 | 396 | 397 | 6. IANA Considerations 398 | 399 | ## New Link Relation Types This document defines the following Link 400 | relation type per [RFC7033]. See section 3 for examples of each type 401 | of Link. 402 | 403 | ### PayID Easy Checkout URL 404 | 405 | * Relation Type ('rel'): `https://payid.org/ns/payid-easy-checkout-uri/1.0` 406 | * Media Type: `application/jrd+json` 407 | * Description: PayID Easy Checkout URL, version 1.0 408 | 409 | 7. Acknowledgments 410 | 411 | 8. Appendix 412 | 413 | 8.1. Motivation 414 | 415 | The PayID Easy Checkout Protocol aims to enable a consistent user 416 | experience for senders paying for goods or services by standardizing 417 | the interaction between merchants/non-profits and customer/donor 418 | wallets. Given the ability to assign arbitrary metadata to a PayID 419 | as defined in [PAYID-DISCOVERY], there is an opportunity to 420 | standardize the set of interactions between merchant and sender, 421 | specifically the process by which a merchant directs a sender to 422 | their digital wallet to complete a payment. The intention of this 423 | protocol is to enable an improved paying experience by reducing the 424 | number of steps a sender must take to complete a transaction. 425 | 426 | PayID Easy Checkout also limits the engineering effort needed to 427 | implement the protocol. Clients wishing to adopt this pattern should 428 | only need to implement UI-level changes in order to make the flow 429 | function as intended, which may aid in expanding overall adoption, 430 | further enhancing the protocol's user experience benefits. 431 | 432 | 8.1.1. Design Goals 433 | 434 | 8.1.1.1. Minimal effort for the Sender 435 | 436 | In order for a sender to checkout using the PayID Easy Checkout 437 | protocol, the sender only needs to provide a merchant with their 438 | PayID Easy Checkout enabled PayID. 439 | 440 | 8.1.1.2. No New Server-Side Software 441 | 442 | Apart from a PayID Discovery compliant PayID Discovery Server, The 443 | PayID Easy Checkout Protocol does not require server-side software to 444 | be run by either the sender or merchant for a payment. The PayID 445 | 446 | 447 | 448 | Kramer, et al. Expires February 7, 2021 [Page 8] 449 | 450 | Internet-Draft Draft 1 - PayID Easy Checkout Protocol August 2020 451 | 452 | 453 | Discovery Server is capable of providing details of where to send the 454 | sender via the PayID Discovery Protocol. Assuming the wallet used by 455 | the sender has implemented support in their UI for the PayID Easy 456 | Checkout Protocol, the sender can be redirected to their wallet to 457 | complete their transaction. 458 | 459 | 8.2. Example Usage 460 | 461 | This section shows a non-normative example of PayID Easy Checkout 462 | between a hypothetical merchant (recipient) and sender. The merchant 463 | accepts payments using the PayID pay$merchant.example.com, and the 464 | sender controls the PayID alice$wallet.example.com. 465 | 466 | 8.2.1. PayID Easy Checkout Initiation 467 | 468 | In this example, the sender might place some items in an online 469 | shopping cart on the merchant's web-site, then choose to checkout. 470 | The merchant would then render a form asking for the sender's PayID, 471 | as well as a "Checkout with PayID" button. Once the sender inputs 472 | their PayID "alice$wallet.example.com" and clicks the "Checkout with 473 | PayID" button, the merchant begins the PayID Easy Checkout flow. 474 | 475 | 8.2.2. PayID Easy Checkout Wallet Discovery 476 | 477 | The merchant UI would first assemble the PayID Easy Checkout URL as 478 | defined in PayID Easy Checkout Discovery (Section 3.1), yielding the 479 | URL "https://wallet.example.com/.well-known/ 480 | webfinger?resource=payid%3Aalice%24wallet.example.com". The merchant 481 | UI would then query the assembled URL (Section 3.1.2). 482 | 483 | The HTTP request in this example would look like this: 484 | 485 | GET /.well-known/webfinger?resource=payid%3Aalice%24wallet.example.com 486 | Host: wallet.example.com 487 | 488 | If the sender's PayID Discovery Server has enabled PayID Easy 489 | Checkout in their wallet, the server would respond with something 490 | like this: 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | Kramer, et al. Expires February 7, 2021 [Page 9] 505 | 506 | Internet-Draft Draft 1 - PayID Easy Checkout Protocol August 2020 507 | 508 | 509 | HTTP/1.1 200 OK 510 | Access-Control-Allow-Origin: * 511 | Content-Type: application/jrd+json 512 | 513 | { 514 | "subject" : "payid:alice$wallet.example.com", 515 | "links" : 516 | [ 517 | { 518 | "rel": "https://payid.org/ns/payid-easy-checkout-uri/1.0", 519 | "template": "https://wallet.example.com/checkout" 520 | } 521 | ] 522 | } 523 | 524 | 8.2.3. Assemble PayID Easy Checkout URL with Query Parameters 525 | 526 | The merchant UI would parse the PayID Discovery response and iterate 527 | over the 'links' collection to find the link with the Relation Type 528 | of "https://payid.org/ns/payid-easy-checkout-uri/1.0". The merchant 529 | UI would then add all of the query parameters defined in PayID Easy 530 | Checkout URL Query Parameters (Section 3.2.1) to the URL included in 531 | the JRD Link. One query parameter of note is the "nextUrl" 532 | parameter, which allows the merchant to supply a redirect or callback 533 | URL for the sender's wallet to call once the sender has confirmed the 534 | payment. In this example, the merchant would like to display a 535 | "Thank You" page, and replaces "{nextUrl}" with 536 | "https://merchant.com/thankyou". 537 | 538 | 8.2.3.1. Correlating a Payment to an Invoice 539 | 540 | Merchants and non-profits will often need to correlate discrete 541 | layer-1 payments to an invoice or transaction entity in the 542 | merchants' native systems. The merchant in this example may have an 543 | invoice tracking system, on which an invoice gets created for the 544 | goods that the sender is buying, for example an invoice with a unique 545 | identifier of "1045464". A common practice for correlating layer-1 546 | payments to a specific transaction or invoice is to accept payments 547 | on a different layer-1 address for each invoice so that the merchant 548 | can listen for payments into that address and correlate the payment 549 | to the invoice. However, because the PayID Easy Checkout URL only 550 | provides the receiver's PayID, there is currently no way to associate 551 | the address that is given to the sender to the invoice. 552 | 553 | In order to accomplish this, a merchant could provide a unique PayID 554 | associated with an invoice, for example a PayID containing the 555 | invoice identifier, for each PayID Easy Checkout transaction. In 556 | this example, the merchant would first associate a payment address 557 | 558 | 559 | 560 | Kramer, et al. Expires February 7, 2021 [Page 10] 561 | 562 | Internet-Draft Draft 1 - PayID Easy Checkout Protocol August 2020 563 | 564 | 565 | with the invoice ID, and would then redirect the sender to their 566 | wallet with the "receiverPayId" query parameter set to "pay- 567 | 1045464$merchant.com". When the merchant PayID Server receives a 568 | query for the address associated with that PayID, they could return 569 | the previously stored payment address. When the merchant receives a 570 | payment to that address, they can then associate the layer 1 payment 571 | with the invoice. 572 | 573 | 8.2.4. Redirect Sender to Their Wallet 574 | 575 | Once the merchant UI populates the required query parameters in the 576 | URL template, the merchant UI redirects the sender to the Redirect 577 | URL so that the sender can confirm the payment. 578 | 579 | 8.2.5. Sender Confirms Payment 580 | 581 | After the sender clicks the "Pay with PayID" button the merchant's 582 | UI, and the merchant performs the previous steps, the sender will be 583 | redirected to the Redirect URL, which is a front end resource of the 584 | wallet. The wallet UI can read the query parameters from the 585 | Redirect URL and render a confirmation page or modal with all of the 586 | required fields pre-populated. 587 | 588 | Once the sender confirms the payment, the wallet would perform a 589 | PayID address lookup on the "receiverPayId" query parameter to get 590 | the payment address of the merchant and submit a transaction to the 591 | underlying ledger or payment system. The merchant can then redirect 592 | the user back to the URL specified in the "nextUrl" query parameter, 593 | which will display the "Thank You" page of the merchant. 594 | 595 | 9. References 596 | 597 | 9.1. Normative References 598 | 599 | [PAYID-DISCOVERY] 600 | Fuelling, D., "PayID Discovery", n.d.. 601 | 602 | [PAYID-PROTOCOL] 603 | Schwartz, D., "PayID Protocol", n.d.. 604 | 605 | [PAYID-URI] 606 | Fuelling, D., "The 'payid' URI Scheme", n.d., 607 | . 608 | 609 | [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate 610 | Requirement Levels", BCP 14, RFC 2119, 611 | DOI 10.17487/RFC2119, March 1997, 612 | . 613 | 614 | 615 | 616 | Kramer, et al. Expires February 7, 2021 [Page 11] 617 | 618 | Internet-Draft Draft 1 - PayID Easy Checkout Protocol August 2020 619 | 620 | 621 | [RFC2818] Rescorla, E., "HTTP Over TLS", RFC 2818, 622 | DOI 10.17487/RFC2818, May 2000, 623 | . 624 | 625 | [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform 626 | Resource Identifier (URI): Generic Syntax", STD 66, 627 | RFC 3986, DOI 10.17487/RFC3986, January 2005, 628 | . 629 | 630 | [RFC6265] Barth, A., "HTTP State Management Mechanism", RFC 6265, 631 | DOI 10.17487/RFC6265, April 2011, 632 | . 633 | 634 | [RFC6570] Gregorio, J., Fielding, R., Hadley, M., Nottingham, M., 635 | and D. Orchard, "URI Template", RFC 6570, 636 | DOI 10.17487/RFC6570, March 2012, 637 | . 638 | 639 | [RFC7033] Jones, P., Salgueiro, G., Jones, M., and J. Smarr, 640 | "WebFinger", RFC 7033, DOI 10.17487/RFC7033, September 641 | 2013, . 642 | 643 | [RFC7231] Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer 644 | Protocol (HTTP/1.1): Semantics and Content", RFC 7231, 645 | DOI 10.17487/RFC7231, June 2014, 646 | . 647 | 648 | [RFC7413] Cheng, Y., Chu, J., Radhakrishnan, S., and A. Jain, "TCP 649 | Fast Open", RFC 7413, DOI 10.17487/RFC7413, December 2014, 650 | . 651 | 652 | [RFC8446] Rescorla, E., "The Transport Layer Security (TLS) Protocol 653 | Version 1.3", RFC 8446, DOI 10.17487/RFC8446, August 2018, 654 | . 655 | 656 | 9.2. Informative References 657 | 658 | [RFC5988] Nottingham, M., "Web Linking", RFC 5988, 659 | DOI 10.17487/RFC5988, October 2010, 660 | . 661 | 662 | 9.3. URIs 663 | 664 | [1] https://payid.org/ 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | Kramer, et al. Expires February 7, 2021 [Page 12] 673 | 674 | Internet-Draft Draft 1 - PayID Easy Checkout Protocol August 2020 675 | 676 | 677 | Authors' Addresses 678 | 679 | Noah Kramer 680 | Ripple 681 | 315 Montgomery Street 682 | San Francisco, CA 94104 683 | US 684 | 685 | Phone: ----------------- 686 | Email: nkramer@ripple.com 687 | URI: https://www.ripple.com 688 | 689 | 690 | David Fuelling 691 | Ripple 692 | 315 Montgomery Street 693 | San Francisco, CA 94104 694 | US 695 | 696 | Phone: ----------------- 697 | Email: fuelling@ripple.com 698 | URI: https://www.ripple.com 699 | 700 | 701 | Ian Simpson 702 | Ripple 703 | 315 Montgomery Street 704 | San Francisco, CA 94104 705 | US 706 | 707 | Phone: ----------------- 708 | Email: isimpson@ripple.com 709 | URI: https://www.ripple.com 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | Kramer, et al. Expires February 7, 2021 [Page 13] 729 | -------------------------------------------------------------------------------- /dist/spec/payid-uri.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Network Working Group D. Fuelling 6 | Internet-Draft Ripple 7 | Intended status: Standards Track July 17, 2020 8 | Expires: January 18, 2021 9 | 10 | 11 | The 'payid' URI Scheme 12 | draft-fuelling-payid-uri-01 13 | 14 | Abstract 15 | 16 | This specification defines the 'payid' Uniform Resource Identifier 17 | (URI) scheme as a way to identify a payment account at a service 18 | provider. 19 | 20 | Feedback 21 | 22 | This specification is a draft proposal, and is part of the PayID 23 | Protocol [1] initiative. Feedback related to this document should be 24 | sent in the form of a Github issue at: https://github.com/payid- 25 | org/rfcs/issues. 26 | 27 | Status of This Memo 28 | 29 | This Internet-Draft is submitted in full conformance with the 30 | provisions of BCP 78 and BCP 79. 31 | 32 | Internet-Drafts are working documents of the Internet Engineering 33 | Task Force (IETF). Note that other groups may also distribute 34 | working documents as Internet-Drafts. The list of current Internet- 35 | Drafts is at https://datatracker.ietf.org/drafts/current/. 36 | 37 | Internet-Drafts are draft documents valid for a maximum of six months 38 | and may be updated, replaced, or obsoleted by other documents at any 39 | time. It is inappropriate to use Internet-Drafts as reference 40 | material or to cite them other than as "work in progress." 41 | 42 | This Internet-Draft will expire on January 18, 2021. 43 | 44 | Copyright Notice 45 | 46 | Copyright (c) 2020 IETF Trust and the persons identified as the 47 | document authors. All rights reserved. 48 | 49 | This document is subject to BCP 78 and the IETF Trust's Legal 50 | Provisions Relating to IETF Documents 51 | (https://trustee.ietf.org/license-info) in effect on the date of 52 | publication of this document. Please review these documents 53 | 54 | 55 | 56 | Fuelling Expires January 18, 2021 [Page 1] 57 | 58 | Internet-Draft The 'payid' URI Scheme July 2020 59 | 60 | 61 | carefully, as they describe your rights and restrictions with respect 62 | to this document. Code Components extracted from this document must 63 | include Simplified BSD License text as described in Section 4.e of 64 | the Trust Legal Provisions and are provided without warranty as 65 | described in the Simplified BSD License. 66 | 67 | Table of Contents 68 | 69 | 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 2 70 | 2. Terminology . . . . . . . . . . . . . . . . . . . . . . . . . 3 71 | 3. Definition . . . . . . . . . . . . . . . . . . . . . . . . . 3 72 | 4. Examples . . . . . . . . . . . . . . . . . . . . . . . . . . 3 73 | 5. Security Concerns . . . . . . . . . . . . . . . . . . . . . . 4 74 | 6. Internationalization Concerns . . . . . . . . . . . . . . . . 5 75 | 7. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 5 76 | 8. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . 6 77 | 9. References . . . . . . . . . . . . . . . . . . . . . . . . . 6 78 | 9.1. Normative References . . . . . . . . . . . . . . . . . . 6 79 | 9.2. Informative References . . . . . . . . . . . . . . . . . 7 80 | 9.3. URIs . . . . . . . . . . . . . . . . . . . . . . . . . . 8 81 | Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 8 82 | 83 | 1. Introduction 84 | 85 | Various Uniform Resource Identifier (URI) schemes can be used to 86 | identify a user account at a service provider. However, no standard 87 | identifier exists to identify a user's _payment_ account at a service 88 | provider. 89 | 90 | While popular URIs could be re-used as payment account identifiers, 91 | these identifiers are insufficient because they are typically 92 | recognized as supporting functionality unique to those schemes. For 93 | example, the 'mailto' scheme [RFC6068] is broadly deployed for 94 | messaging. Re-using this identifier for payments would likely cause 95 | confusion because one desirable quality of a payment account 96 | identifier is that it expressly does not support messaging, in order 97 | to avoid spam and/or other security concerns such as phishing 98 | attacks. 99 | 100 | Deploying payment protocols on top of identifiers that are commonly 101 | employed for other use-cases would likely be a mis-use of those 102 | identifiers, and could also cause confusion for end-users, among 103 | other problems. 104 | 105 | Instead, the 'payid' scheme defines an identifier that is intended to 106 | identify accounts for payment use-cases only. 107 | 108 | 109 | 110 | 111 | 112 | Fuelling Expires January 18, 2021 [Page 2] 113 | 114 | Internet-Draft The 'payid' URI Scheme July 2020 115 | 116 | 117 | 2. Terminology 118 | 119 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 120 | "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and 121 | "OPTIONAL" in this document are to be interpreted as described in 122 | [RFC2119]. 123 | 124 | 3. Definition 125 | 126 | The syntax of the 'payid' URI scheme is defined in Section 7 of this 127 | document. 128 | 129 | A 'payid' URI identifies a payment account hosted at a service 130 | provider, and is designed for payment account identification rather 131 | than interaction, as discussed in section 1.2.2 of [RFC3986]. 132 | 133 | A 'payid' URI is constructed by taking a user's payment account 134 | identifier at a service provider and using that value as the 135 | 'acctpart'. The 'host' portion is then set to the DNS domain name of 136 | the service provider that provides the 'payid'. 137 | 138 | To compare two 'payid' URIs, case normalization and percent-encoding 139 | normalization (as specified in sections 6.2.2.1 and 6.2.2.2 of 140 | [RFC3986]) MUST be employed before performing any comparison. 141 | 142 | In addition, a 'payid' is case-insensitive and therefore should be 143 | normalized to lowercase. For example, the URI 144 | "PAYID:aLICE$www.EXAMPLE.com" is equivalent to 145 | "payid:alice$www.example.com". 146 | 147 | Note that both the 'acctpart' and 'host' components of a 'payid' may 148 | contain one or more dollar-sign characters. However, because a 149 | 'host' SHOULD also be a valid DNS domain, that portion of a 'payid' 150 | will generally not include a dollar-sign. Therefore, applications 151 | SHOULD always search for the last dollar-sign when attempting to 152 | parse a 'payid' URI into its two component parts. 153 | 154 | 4. Examples 155 | 156 | As an example, a user with an account name of "apollo" at a wallet 157 | service "wallet.example.com" can be identified by a URI using the 158 | 'payid' scheme via the following construction: 159 | 160 | 'payid:apollo$wallet.example.com' 161 | 162 | One possible PayID scenario is for an account to be registered with a 163 | payment service provider using an identifier that is associated with 164 | some other service provider. For example, a user with the email 165 | 166 | 167 | 168 | Fuelling Expires January 18, 2021 [Page 3] 169 | 170 | Internet-Draft The 'payid' URI Scheme July 2020 171 | 172 | 173 | address "alice@example.net" might register with a wallet website 174 | whose domain name is "wallet.example.com". In order to facilitate 175 | payments to/from Alice, the wallet service provider might offer Alice 176 | a PayID using Alice's email address (though using an email address as 177 | a PayID is not recommended). In order to use Alice's email address 178 | as the 'acctpart' of the 'payid' URI, no percent-encoding is 179 | necessary because the 'acctpart' portion of a PayID allows for at- 180 | signs. Thus, the provisioned 'payid' URI for Alice would be 181 | "payid:alice@example.net$shoppingsite.example". 182 | 183 | Another possible scenario is where a payment service provider (e.g., 184 | a digital wallet) provides its users with PayIDs that are associated 185 | with the PayIDs of another service provider. For example, a user 186 | with the PayID "alice$bank.example.net" might register with a wallet 187 | website whose domain name is "wallet.example.net". In order to use 188 | the bank's PayID as the acctpart of the wallet's 'payid' URI, no 189 | percent-encoding is necessary because the 'acctpart' portion of a 190 | PayID allows for dollar-signs. Therefore, the resulting 'payid' URI 191 | would be "payid:alice$bank.example$wallet.example". 192 | 193 | The following example URIs illustrate several variations of PayIDs 194 | and their common syntax components: 195 | 196 | payid:alice$example.net 197 | 198 | payid:john.doe$example.net 199 | 200 | payid:jane-doe$example.net 201 | 202 | 5. Security Concerns 203 | 204 | The 'payid' URI scheme defined in this document does not directly 205 | enable interaction with a user's payment account and therefore does 206 | not present any direct security concerns. 207 | 208 | However, a 'payid' URI indicates existence of a payment account, so 209 | care should be taken to properly secure any payment account 210 | interactions allowed by a service provider. 211 | 212 | In addition, service providers and users should consider whether an 213 | attacker might be able to derive or infer other identifiers 214 | correlating to the user of any particular PayID. For example, 215 | replacing the "$" character in a PayID with an "@" sign SHOULD NOT 216 | yield a 'mailto' URI, when possible. In addition, care should be 217 | taken when the 'acctpart' of a PayID corresponds to a user's email 218 | address (in part or in whole) as this might allow an attacker to 219 | execute phishing attacks or send spam messages. 220 | 221 | 222 | 223 | 224 | Fuelling Expires January 18, 2021 [Page 4] 225 | 226 | Internet-Draft The 'payid' URI Scheme July 2020 227 | 228 | 229 | Due to the use of percent-encoding in 'payid' URIs, implementers 230 | SHOULD disallow percent-encoded characters or sequences that would 231 | result in "space", "null", "control", or other characters that are 232 | otherwise forbidden. 233 | 234 | 6. Internationalization Concerns 235 | 236 | As specified in [RFC3986], the 'payid' URI scheme allows any 237 | character from the Unicode repertoire [UNICODE] encoded as UTF-8 238 | [RFC3629] and then percent-encoded into valid ASCII [RFC0020]. 239 | Before applying any percent-encoding, an application MUST ensure the 240 | following about the string that is used as input to the URI- 241 | construction process: 242 | 243 | o The 'acctpart' consists only of Unicode code points that conform 244 | to the PRECIS IdentifierClass specified in [RFC8264]. 245 | 246 | o The 'host' consists only of Unicode code points that conform to 247 | the rules specified in [RFC5892]. 248 | 249 | o Internationalized domain name (IDN) labels are encoded as A-labels 250 | [RFC5890]. 251 | 252 | 7. IANA Considerations 253 | 254 | In accordance with [RFC7595], this section provides the information 255 | needed to register the 'payid' URI scheme. 256 | 257 | *URI Scheme Name*: payid 258 | 259 | *Status*: permanent 260 | 261 | *URI Scheme Syntax*: The 'payid' URI syntax is defined here in 262 | Augmented Backus-Naur Form (ABNF) per [RFC5234], borrowing the 'host' 263 | and 'path' rules from [RFC3986]: 264 | 265 | payidURI = "payid" ":" acctpart "$" host 266 | acctpart = path 267 | 268 | Note that additional rules limit the characters that can be percent- 269 | encoded in a 'payid' URI. See "Encoding Considerations" below for 270 | more details. 271 | 272 | *URI Scheme Semantics*: The 'payid' URI scheme identifies payment 273 | accounts hosted at payment service providers. It is used only for 274 | identification, not interaction. 275 | 276 | *Encoding Considerations*: See Section 6 of this document. 277 | 278 | 279 | 280 | Fuelling Expires January 18, 2021 [Page 5] 281 | 282 | Internet-Draft The 'payid' URI Scheme July 2020 283 | 284 | 285 | *Applications/Protocols That Use This URI Scheme Name*: The following 286 | protocols utilize this URI scheme: 287 | 288 | - [PAYID-DISCOVERY][], 289 | - [PAYID-PROTOCOL][], 290 | - [VERIFIABLE-PAYID][]. 291 | 292 | *Interoperability Considerations*: n/a. 293 | 294 | *Security Considerations*: See Section 6 of this document. 295 | 296 | *Contact*: rfcs@payid.org 297 | 298 | *Author/Change Controller*: TBD. 299 | 300 | *References*: None. 301 | 302 | 8. Acknowledgements 303 | 304 | This document was adapted from and heavily influenced by [RFC7565], 305 | modifying it (in some cases only slightly) for a payments use-case. 306 | The author would like to acknowledge the contributions of everyone 307 | who worked on that and related specifications. 308 | 309 | In addition, the author would like to acknowledge everyone who 310 | provided feedback and use-cases for this derivative specification. 311 | 312 | 9. References 313 | 314 | 9.1. Normative References 315 | 316 | [PAYID-DISCOVERY] 317 | Fuelling, D., "The PayID Discovery Protocol", n.d., 318 | . 319 | 320 | [PAYID-PROTOCOL] 321 | Malhotra, A. and D. Schwartz, "PayID Protocol", n.d., 322 | . 323 | 324 | [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate 325 | Requirement Levels", BCP 14, RFC 2119, 326 | DOI 10.17487/RFC2119, March 1997, 327 | . 328 | 329 | [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO 330 | 10646", STD 63, RFC 3629, DOI 10.17487/RFC3629, November 331 | 2003, . 332 | 333 | 334 | 335 | 336 | Fuelling Expires January 18, 2021 [Page 6] 337 | 338 | Internet-Draft The 'payid' URI Scheme July 2020 339 | 340 | 341 | [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform 342 | Resource Identifier (URI): Generic Syntax", STD 66, 343 | RFC 3986, DOI 10.17487/RFC3986, January 2005, 344 | . 345 | 346 | [RFC5234] Crocker, D., Ed. and P. Overell, "Augmented BNF for Syntax 347 | Specifications: ABNF", STD 68, RFC 5234, 348 | DOI 10.17487/RFC5234, January 2008, 349 | . 350 | 351 | [RFC5890] Klensin, J., "Internationalized Domain Names for 352 | Applications (IDNA): Definitions and Document Framework", 353 | RFC 5890, DOI 10.17487/RFC5890, August 2010, 354 | . 355 | 356 | [RFC5892] Faltstrom, P., Ed., "The Unicode Code Points and 357 | Internationalized Domain Names for Applications (IDNA)", 358 | RFC 5892, DOI 10.17487/RFC5892, August 2010, 359 | . 360 | 361 | [RFC8264] Saint-Andre, P. and M. Blanchet, "PRECIS Framework: 362 | Preparation, Enforcement, and Comparison of 363 | Internationalized Strings in Application Protocols", 364 | RFC 8264, DOI 10.17487/RFC8264, October 2017, 365 | . 366 | 367 | [UNICODE] Consortium, T. U., "The Unicode Standard", n.d., 368 | . 369 | 370 | [VERIFIABLE-PAYID] 371 | Malhotra, A. and D. Schwartz, "Verifiable PayID Protocol", 372 | n.d., . 373 | 374 | 9.2. Informative References 375 | 376 | [RFC0020] Cerf, V., "ASCII format for network interchange", STD 80, 377 | RFC 20, DOI 10.17487/RFC0020, October 1969, 378 | . 379 | 380 | [RFC5988] Nottingham, M., "Web Linking", RFC 5988, 381 | DOI 10.17487/RFC5988, October 2010, 382 | . 383 | 384 | [RFC6068] Duerst, M., Masinter, L., and J. Zawinski, "The 'mailto' 385 | URI Scheme", RFC 6068, DOI 10.17487/RFC6068, October 2010, 386 | . 387 | 388 | 389 | 390 | 391 | 392 | Fuelling Expires January 18, 2021 [Page 7] 393 | 394 | Internet-Draft The 'payid' URI Scheme July 2020 395 | 396 | 397 | [RFC7033] Jones, P., Salgueiro, G., Jones, M., and J. Smarr, 398 | "WebFinger", RFC 7033, DOI 10.17487/RFC7033, September 399 | 2013, . 400 | 401 | [RFC7565] Saint-Andre, P., "The 'acct' URI Scheme", RFC 7565, 402 | DOI 10.17487/RFC7565, May 2015, 403 | . 404 | 405 | [RFC7595] Thaler, D., Ed., Hansen, T., and T. Hardie, "Guidelines 406 | and Registration Procedures for URI Schemes", BCP 35, 407 | RFC 7595, DOI 10.17487/RFC7595, June 2015, 408 | . 409 | 410 | 9.3. URIs 411 | 412 | [1] https://payid.org/ 413 | 414 | Author's Address 415 | 416 | David Fuelling 417 | Ripple 418 | 315 Montgomery Street 419 | San Francisco, CA 94104 420 | US 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | Fuelling Expires January 18, 2021 [Page 8] 449 | -------------------------------------------------------------------------------- /dist/spec/payid-uri.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ]> 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | The 'payid' URI Scheme 30 | 31 | 32 | Ripple 33 |
34 | 35 | 315 Montgomery Street 36 | San Francisco 37 | CA 38 | 94104 39 | US 40 | 41 |
42 |
43 | 44 | 45 | 46 | security 47 | 48 | 49 | 50 | 51 | 52 | 53 | This specification defines the 'payid' Uniform Resource Identifier (URI) 54 | scheme as a way to identify a payment account at a service provider. 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | This specification is a draft proposal, and is part of the 65 | PayID Protocol initiative. Feedback related to this 66 | document should be sent in the form of a Github issue at: 67 | https://github.com/payid-org/rfcs/issues. 68 | 69 | 70 | 71 | 72 | 73 |
74 | 75 | 76 | 77 | 78 |
79 | Various Uniform Resource Identifier (URI) schemes can be used to 80 | identify a user account at a service provider. However, no standard 81 | identifier exists to identify a user's payment account at a service 82 | provider. 83 | 84 | While popular URIs could be re-used as payment account identifiers, 85 | these identifiers are insufficient because they are typically recognized 86 | as supporting functionality unique to those schemes. For example, the 87 | 'mailto' scheme is broadly deployed for messaging. Re-using 88 | this identifier for payments would likely cause confusion because one 89 | desirable quality of a payment account identifier is that it expressly 90 | does not support messaging, in order to avoid spam and/or other security 91 | concerns such as phishing attacks. 92 | 93 | Deploying payment protocols on top of identifiers that are commonly 94 | employed for other use-cases would likely be a mis-use of those 95 | identifiers, and could also cause confusion for end-users, among other 96 | problems. 97 | 98 | Instead, the 'payid' scheme defines an identifier that is intended to 99 | identify accounts for payment use-cases only. 100 | 101 |
102 |
103 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 104 | "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and 105 | "OPTIONAL" in this document are to be interpreted as described in 106 | . 107 | 108 |
109 |
110 | The syntax of the 'payid' URI scheme is defined in Section 7 of this 111 | document. 112 | 113 | A 'payid' URI identifies a payment account hosted at a service provider, 114 | and is designed for payment account identification rather than 115 | interaction, as discussed in section 1.2.2 of . 116 | 117 | A 'payid' URI is constructed by taking a user's payment account identifier 118 | at a service provider and using that value as the 'acctpart'. The 'host' 119 | portion is then set to the DNS domain name of the service provider that 120 | provides the 'payid'. 121 | 122 | To compare two 'payid' URIs, case normalization and percent-encoding 123 | normalization (as specified in sections 6.2.2.1 and 6.2.2.2 of 124 | ) MUST be employed before performing any comparison. 125 | 126 | In addition, a 'payid' is case-insensitive and therefore should be 127 | normalized to lowercase. For example, the URI 128 | "PAYID:aLICE$www.EXAMPLE.com" is equivalent to 129 | "payid:alice$www.example.com". 130 | 131 | Note that both the 'acctpart' and 'host' components of a 'payid' may 132 | contain one or more dollar-sign characters. However, because a 'host' 133 | SHOULD also be a valid DNS domain, that portion of a 'payid' will 134 | generally not include a dollar-sign. Therefore, applications SHOULD 135 | always search for the last dollar-sign when attempting to parse a 'payid' 136 | URI into its two component parts. 137 | 138 |
139 |
140 | As an example, a user with an account name of "apollo" at a wallet 141 | service "wallet.example.com" can be identified by a URI using the 'payid' 142 | scheme via the following construction: 143 | 144 |
147 | 148 | One possible PayID scenario is for an account to be registered with a 149 | payment service provider using an identifier that is associated with some 150 | other service provider. For example, a user with the email address 151 | "alice@example.net" might register with a wallet website whose domain 152 | name is "wallet.example.com". In order to facilitate payments to/from 153 | Alice, the wallet service provider might offer Alice a PayID using Alice's 154 | email address (though using an email address as a PayID is not 155 | recommended). In order to use Alice's email address as the 'acctpart' of 156 | the 'payid' URI, no percent-encoding is necessary because the 'acctpart' 157 | portion of a PayID allows for at-signs. Thus, the provisioned 'payid' URI 158 | for Alice would be "payid:alice@example.net$shoppingsite.example". 159 | 160 | Another possible scenario is where a payment service provider (e.g., a 161 | digital wallet) provides its users with PayIDs that are associated with 162 | the PayIDs of another service provider. For example, a user with the 163 | PayID "alice$bank.example.net" might register with a wallet website whose 164 | domain name is "wallet.example.net". In order to use the bank's PayID 165 | as the acctpart of the wallet's 'payid' URI, no percent-encoding is 166 | necessary because the 'acctpart' portion of a PayID allows for 167 | dollar-signs. Therefore, the resulting 'payid' URI would be 168 | "payid:alice$bank.example$wallet.example". 169 | 170 | The following example URIs illustrate several variations of PayIDs and 171 | their common syntax components: 172 | 173 |
180 | 181 |
182 |
183 | The 'payid' URI scheme defined in this document does not directly enable 184 | interaction with a user's payment account and therefore does not present 185 | any direct security concerns. 186 | 187 | However, a 'payid' URI indicates existence of a payment account, so 188 | care should be taken to properly secure any payment account interactions 189 | allowed by a service provider. 190 | 191 | In addition, service providers and users should consider whether an 192 | attacker might be able to derive or infer other identifiers correlating 193 | to the user of any particular PayID. For example, replacing the $ 194 | character in a PayID with an @ sign SHOULD NOT yield a 'mailto' URI, 195 | when possible. In addition, care should be taken when the 'acctpart' of 196 | a PayID corresponds to a user's email address (in part or in whole) as this 197 | might allow an attacker to execute phishing attacks or send spam messages. 198 | 199 | Due to the use of percent-encoding in 'payid' URIs, implementers SHOULD 200 | disallow percent-encoded characters or sequences that would result in 201 | "space", "null", "control", or other characters that are otherwise 202 | forbidden. 203 | 204 |
205 |
206 | As specified in , the 'payid' URI scheme allows any character 207 | from the Unicode repertoire encoded as UTF-8 and 208 | then percent-encoded into valid ASCII . Before applying any 209 | percent-encoding, an application MUST ensure the following about the 210 | string that is used as input to the URI-construction process: 211 | 212 | 213 | The 'acctpart' consists only of Unicode code points that conform to 214 | the PRECIS IdentifierClass specified in . 215 | The 'host' consists only of Unicode code points that conform to the 216 | rules specified in . 217 | Internationalized domain name (IDN) labels are encoded as A-labels 218 | . 219 | 220 | 221 |
222 |
223 | In accordance with , this section provides the information 224 | needed to register the 'payid' URI scheme. 225 | 226 | URI Scheme Name: payid 227 | 228 | Status: permanent 229 | 230 | URI Scheme Syntax: The 'payid' URI syntax is defined here in Augmented 231 | Backus-Naur Form (ABNF) per , borrowing the 'host' and 'path' 232 | rules from : 233 | 234 |
238 | 239 | Note that additional rules limit the characters that can be 240 | percent-encoded in a 'payid' URI. See "Encoding Considerations" below for 241 | more details. 242 | 243 | URI Scheme Semantics: The 'payid' URI scheme identifies payment 244 | accounts hosted at payment service providers. It is used only for 245 | identification, not interaction. 246 | 247 | Encoding Considerations: See Section 6 of this document. 248 | 249 | Applications/Protocols That Use This URI Scheme Name: The following 250 | protocols utilize this URI scheme: 251 | 252 |
257 | 258 | Interoperability Considerations: n/a. 259 | 260 | Security Considerations: See Section 6 of this document. 261 | 262 | Contact: rfcs@payid.org 263 | 264 | Author/Change Controller: TBD. 265 | 266 | References: None. 267 | 268 |
269 |
270 | This document was adapted from and heavily influenced by , 271 | modifying it (in some cases only slightly) for a payments use-case. The 272 | author would like to acknowledge the contributions of everyone who worked 273 | on that and related specifications. 274 | 275 | In addition, the author would like to acknowledge everyone who provided 276 | feedback and use-cases for this derivative specification. 277 | 278 |
279 | 280 | 281 |
282 | 283 | 284 | 285 | 286 | 287 | &RFC2119; 288 | &RFC3629; 289 | &RFC3986; 290 | &RFC5234; 291 | &RFC5890; 292 | &RFC5892; 293 | &RFC8264; 294 | 295 | 296 | The PayID Discovery Protocol 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | PayID Protocol 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | Verifiable PayID Protocol 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | The Unicode Standard 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | &RFC0020; 343 | &RFC5988; 344 | &RFC6068; 345 | &RFC7033; 346 | &RFC7565; 347 | &RFC7595; 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 443 | 444 |
445 | 446 | -------------------------------------------------------------------------------- /java/README.md: -------------------------------------------------------------------------------- 1 | # PayID Java Test Harness 2 | This folder contains a Java implementation of a PayID, in addition to a 3 | test harness that validates the JSON test vectors. 4 | 5 | NOTE that the PayID code in this project will eventually be moved to xpring4j 6 | , but the test-harness and test-vectors will continue to live in this RFC. 7 | 8 | ## Running Tests 9 | 1. Install [Maven](https://maven.apache.org/) and a [JDK](https://www.oracle.com/java/technologies/javase-downloads.html). 10 | 1. Execute this command: `mvn clean install` in the folder holding this 11 | README file. 12 | -------------------------------------------------------------------------------- /java/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | PayID :: Core 8 | Core libraries for PayID. 9 | org.payid 10 | payid-core 11 | HEAD-SNAPSHOT 12 | jar 13 | 14 | https://github.com/xpring-eng/rfcs/java 15 | 2017 16 | 17 | 18 | 19 | The Apache Software License, Version 2.0 20 | http://www.apache.org/licenses/LICENSE-2.0.txt 21 | repo 22 | 23 | 24 | 25 | 26 | 1.8 27 | 1.8 28 | UTF-8 29 | UTF-8 30 | UTF-8 31 | 2.10.3 32 | 4.2.0 33 | 34 | 35 | 36 | 37 | com.fasterxml.jackson.core 38 | jackson-databind 39 | 40 | 41 | com.google.guava 42 | guava 43 | 44 | 45 | junit 46 | junit 47 | test 48 | 49 | 50 | org.immutables 51 | value 52 | provided 53 | 54 | 55 | org.assertj 56 | assertj-core 57 | test 58 | 59 | 60 | com.squareup.okhttp3 61 | okhttp 62 | test 63 | 64 | 65 | 66 | 67 | 68 | com.fasterxml.jackson.core 69 | jackson-databind 70 | ${jackson.version} 71 | 72 | 73 | com.google.guava 74 | guava 75 | 28.1-android 76 | 77 | 78 | junit 79 | junit 80 | 4.12 81 | test 82 | 83 | 84 | org.immutables 85 | value 86 | 2.8.3 87 | provided 88 | 89 | 90 | org.assertj 91 | assertj-core 92 | 3.13.2 93 | test 94 | 95 | 96 | com.squareup.okhttp3 97 | okhttp 98 | ${okhttp.version} 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /java/src/main/java/org/payid/AbstractPayId.java: -------------------------------------------------------------------------------- 1 | package org.payid; 2 | 3 | import static java.lang.String.format; 4 | 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 7 | import com.google.common.base.Preconditions; 8 | import org.immutables.value.Value; 9 | import org.immutables.value.Value.Check; 10 | 11 | import java.util.regex.Matcher; 12 | import java.util.regex.Pattern; 13 | 14 | /** 15 | * An abstract implementation of {@link PayId} for use by Immutables. 16 | */ 17 | @Value.Immutable 18 | @JsonSerialize(as = ImmutablePayId.class) 19 | @JsonDeserialize(as = ImmutablePayId.class) 20 | abstract class AbstractPayId implements PayId { 21 | 22 | private static final String ALPHA = "a-zA-Z"; 23 | private static final String DIGIT = "0-9"; 24 | private static final String UNRESERVED = ALPHA + DIGIT + "\\-\\.\\_\\~"; 25 | private static final String SUB_DELIMS = "\\!\\$\\&\\'\\(\\)\\*\\+\\,\\;\\="; 26 | private static final String PERCENT = "\\%"; 27 | 28 | // Note: These regex's don't enforce any ordering or format for things like IP address, etc. They merely define 29 | // allowed characters in a PayID. A PayID must be dereferenced in order to determine if it's a valid host 30 | // per RFC-3986. 31 | 32 | // ACCOUNT (allowed-chars) 33 | private static final String ALLOWED_ACCOUNT_CHARS = UNRESERVED + SUB_DELIMS + PERCENT + ":@/"; 34 | private static final String ALLOWED_ACCOUNT_CHARS_REGEX = "^([" + ALLOWED_ACCOUNT_CHARS + "]+)*$"; 35 | 36 | // HOST (allowed-chars) 37 | // IP-literal / IPv4address / reg-name 38 | private static final String HEX_DIGITS = "[[:xdigit:]]"; 39 | private static final String IPV4_ADDRESS = DIGIT + "."; // Hex is already allowed via IP_LITERAL 40 | private static final String IPV6_ADDRESS = ":\\." + HEX_DIGITS; 41 | // reg-name syntax allows percent-encoded octets in order to represent non-ASCII registered names in a uniform way 42 | // that is independent of the underlying name resolution technology 43 | private static final String REG_NAME = UNRESERVED + PERCENT + SUB_DELIMS; 44 | 45 | // IPVFUTURE and IP_LITERAL are not accounted for. 46 | private static final String ALLOWED_HOST_CHARS = IPV4_ADDRESS + IPV6_ADDRESS + REG_NAME; 47 | private static final String ALLOWED_HOST_CHARS_REGEX = "^([" + ALLOWED_HOST_CHARS + "]+)*$"; 48 | 49 | // Regex 50 | private static final Pattern ACCOUNT_PATTERN = Pattern.compile(ALLOWED_ACCOUNT_CHARS_REGEX); 51 | private static final Pattern HOST_PATTERN = Pattern.compile(ALLOWED_HOST_CHARS_REGEX); 52 | 53 | // For upper-casing HEX 54 | private static final Pattern PERCENT_ENCODED_PATTERN = Pattern.compile("(%[0-9a-fA-F][0-9a-fA-F])"); 55 | 56 | /** 57 | * For a String {@code input}, find any lower-cased percent-encoded values (e.g., %3a) and upper-case them (e.g., %3A) 58 | * in order to conform to the rules of RFC-3986. 59 | * 60 | * @param input A {@link String} that might have percent-encoded triplets. 61 | * 62 | * @return A {@link String} with any percent-encoded triplets normalized to upper-case form. 63 | * 64 | * @see "https://tools.ietf.org/html/rfc3986#section-6.2.2.1" 65 | */ 66 | static String upperCasePercentEncoded(String input) { 67 | // Upper-case any percent-encoded triplets (e.g., `%3a` -> `%3A`). 68 | final Matcher matcher = PERCENT_ENCODED_PATTERN.matcher(input); 69 | 70 | final StringBuffer result = new StringBuffer(); 71 | while (matcher.find()) { 72 | matcher.appendReplacement(result, matcher.group().toUpperCase()); 73 | } 74 | matcher.appendTail(result); 75 | 76 | String returnable = result.toString(); 77 | return returnable; 78 | } 79 | 80 | /** 81 | * Validate a PayID per the rules defined in the PayID RFC. 82 | * 83 | * @return A normalized and valid {@link AbstractPayId}. 84 | */ 85 | @Check 86 | AbstractPayId validate() { 87 | 88 | // Verify Account 89 | Preconditions.checkArgument( 90 | ACCOUNT_PATTERN.matcher(this.account()).matches(), 91 | format("PayID 'account' for `%s` has an invalid value.", this.toString()) 92 | ); 93 | 94 | // Verify Host 95 | Preconditions.checkArgument(HOST_PATTERN.matcher(this.host()).matches(), 96 | format("PayID 'host' for `%s` has an invalid value.", this.toString())); 97 | 98 | return this; 99 | } 100 | 101 | @Override 102 | public String toString() { 103 | return PAYID_SCHEME + account() + "$" + host(); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /java/src/main/java/org/payid/PayId.java: -------------------------------------------------------------------------------- 1 | package org.payid; 2 | 3 | import static java.lang.String.format; 4 | import static org.payid.AbstractPayId.upperCasePercentEncoded; 5 | 6 | import com.google.common.base.Preconditions; 7 | import org.immutables.value.Value.Default; 8 | 9 | import java.util.Locale; 10 | import java.util.Objects; 11 | 12 | /** 13 | * A standardized identifier for payment accounts. 14 | * 15 | * @see "https://github.com/xpring-eng/rfcs/blob/master/TBD.md" 16 | */ 17 | public interface PayId { 18 | 19 | String PAYID_SCHEME = "payid:"; 20 | 21 | static ImmutablePayId.Builder builder() { 22 | return ImmutablePayId.builder(); 23 | } 24 | 25 | /** 26 | *

Parses a PayId URI string into a @{code PayId}, applying normalization rules defined in the PayID RFC.

27 | * 28 | *

Normalization includes the following: 29 | * 30 | *

    31 | *
  • Lower-case the scheme, if present.
  • 32 | *
  • Lower-case the accountpart
  • 33 | *
  • Lower-case the host
  • 34 | *
  • For any hex-encoded String, upper-case the Hexadecimal letters (e.g., 'f' -> 'F')
  • 35 | *
36 | * 37 | * @param value text of a complete PayID. 38 | * 39 | * @return A valid {@link PayId}. 40 | * 41 | * @throws NullPointerException if {@code value} is null. 42 | * @throws IllegalArgumentException if {@code value} cannot be properly parsed or has invalid characters per the PayID 43 | * RFC. 44 | */ 45 | static PayId of(String value) { 46 | Objects.requireNonNull(value, "PayID must not be null"); 47 | if (value.toLowerCase(Locale.ENGLISH).startsWith(PAYID_SCHEME)) { 48 | value = value.substring(6); 49 | } else { 50 | throw new IllegalArgumentException(format("PayID `%s` must start with the 'payid:' scheme", value)); 51 | } 52 | 53 | if (!value.contains("$")) { 54 | throw new IllegalArgumentException(format("PayID `%s` must contain a $", value)); 55 | } else { 56 | Preconditions 57 | .checkArgument(value.length() > 6, format("PayID `%s` must specify a valid account and host", value)); 58 | } 59 | 60 | int lastDollar = value.lastIndexOf("$"); 61 | String account = value.substring(0, lastDollar); 62 | String host = value.substring(lastDollar + 1); 63 | 64 | // NOTE: This implementation purposefully does not percent-encode any invalid characters because we don't want to 65 | // be too proscriptive around which encoding scheme should be used. Thus, it is assumed that only valid characters 66 | // are initially supplied to a PayID, and that software encodes properly to the PayID-allowed character set before 67 | // contruction. 68 | 69 | // NORMALIZATION: Capitalization 70 | account = account.toLowerCase(Locale.ENGLISH); 71 | host = host.toLowerCase(Locale.ENGLISH); 72 | 73 | // NORMALIZATION: Percent-encoding 74 | account = upperCasePercentEncoded(account); 75 | host = upperCasePercentEncoded(host); 76 | 77 | final ImmutablePayId.Builder builder = builder(); 78 | if (account.length() > 0) { 79 | builder.account(account); 80 | } 81 | 82 | builder.host(host); 83 | 84 | return builder.build(); 85 | } 86 | 87 | /** 88 | * A payment account identifier defined in the `payid-uri` RFC. 89 | * 90 | * @return A {@link String} containing the 'path' portion of this PayId. 91 | */ 92 | @Default 93 | default String account() { 94 | return ""; 95 | } 96 | 97 | /** 98 | * A host as defined defined in the `payid-uri` RFC. 99 | * 100 | * @return A {@link String} containing the 'host' portion of this PayId. 101 | */ 102 | String host(); 103 | } 104 | -------------------------------------------------------------------------------- /java/src/test/java/org/payid/PayIdInvalidValuesTest.java: -------------------------------------------------------------------------------- 1 | package org.payid; 2 | 3 | import org.junit.Rule; 4 | import org.junit.Test; 5 | import org.junit.rules.ExpectedException; 6 | import org.junit.runner.RunWith; 7 | import org.junit.runners.Parameterized; 8 | import org.junit.runners.Parameterized.Parameters; 9 | 10 | import java.util.Arrays; 11 | import java.util.Collection; 12 | import java.util.Objects; 13 | 14 | /** 15 | * Unit tests for {@link PayId}. 16 | */ 17 | @RunWith(Parameterized.class) 18 | public class PayIdInvalidValuesTest { 19 | 20 | @Rule 21 | public ExpectedException expectedException = ExpectedException.none(); 22 | 23 | private String sourcepayid; 24 | private Class exceptionClassToExpect; 25 | private String exceptionMessageToExpect; 26 | 27 | public PayIdInvalidValuesTest( 28 | final String sourcepayid, final Class exceptionToExpect, final String exceptionMessageToExpect 29 | ) { 30 | this.sourcepayid = sourcepayid; 31 | this.exceptionClassToExpect = Objects.requireNonNull(exceptionToExpect); 32 | this.exceptionMessageToExpect = exceptionMessageToExpect; 33 | } 34 | 35 | /** 36 | * The data for this test... 37 | */ 38 | @Parameters 39 | public static Collection data() { 40 | 41 | return Arrays.asList(new Object[][]{ 42 | 43 | ///////////////// 44 | // Invalid Values 45 | ///////////////// 46 | 47 | //0 48 | { 49 | null, // input 50 | NullPointerException.class, // exception to expect 51 | "PayID must not be null", // message to expect 52 | }, 53 | //1 54 | { 55 | "$", // input (missing host) 56 | IllegalArgumentException.class, // exception to expect 57 | "PayID `$` must start with the 'payid:' scheme", // message to expect 58 | }, 59 | //2 60 | { 61 | "payid:$", // input (missing host) 62 | IllegalArgumentException.class, // exception to expect 63 | "PayID `$` must specify a valid account and host", // message to expect 64 | }, 65 | //3 66 | { 67 | "alice$wallet.example", // input missing payid: prefix 68 | IllegalArgumentException.class, // exception to expect 69 | "PayID `alice$wallet.example` must start with the 'payid:' scheme", // message to expect 70 | }, 71 | //4 72 | { 73 | "payid:alice$nic.書籍", // input 74 | IllegalArgumentException.class, // exception to expect 75 | "PayID 'host' for `payid:alice$nic.書籍` has an invalid value.",// message to expect 76 | }, 77 | //5 (No IDNs). In order to work in a PayID, an IDN must be encoded properly using PUNY-Code or some other 78 | // mechanism that can translate into a value PayID. 79 | { 80 | "payid:書籍$example.com", // input 81 | IllegalArgumentException.class, // exception to expect 82 | "PayID 'account' for `payid:書籍$example.com` has an invalid value.",// message to expect 83 | }, 84 | //6 (don't accept gen-delims "/") 85 | { 86 | "payid:/$example.com", // input 87 | IllegalArgumentException.class, // exception to expect 88 | "PayID 'account' for `payid:/$example.com` has an invalid value.",// message to expect 89 | }, 90 | //7 (don't accept gen-delims "/") 91 | { 92 | "payid:alice$/example.com", // input 93 | IllegalArgumentException.class, // exception to expect 94 | "PayID 'host' for `payid:alice$/example.com` has an invalid value.",// message to expect 95 | }, 96 | //8 (don't accept gen-delims "?") 97 | { 98 | "payid:?$example.com", // input 99 | IllegalArgumentException.class, // exception to expect 100 | "PayID 'account' for `payid:?$example.com` has an invalid value.",// message to expect 101 | }, 102 | //9 (don't accept gen-delims "?") 103 | { 104 | "payid:a$?example.com", // input 105 | IllegalArgumentException.class, // exception to expect 106 | "PayID 'host' for `payid:a$?example.com` has an invalid value.",// message to expect 107 | }, 108 | //10 (don't accept gen-delims "#") 109 | { 110 | "payid:#$example.com", // input 111 | IllegalArgumentException.class, // exception to expect 112 | "PayID 'account' for `payid:#$example.com` has an invalid value.",// message to expect 113 | }, 114 | //11 (don't accept gen-delims "#") 115 | { 116 | "payid:a$#example.com", // input 117 | IllegalArgumentException.class, // exception to expect 118 | "PayID 'host' for `payid:a$#example.com` has an invalid value.",// message to expect 119 | }, 120 | //12 (don't accept gen-delims "[" in account) 121 | { 122 | "payid:[$example.com", // input 123 | IllegalArgumentException.class, // exception to expect 124 | "PayID 'account' for `payid:[$example.com` has an invalid value.",// message to expect 125 | }, 126 | //13 (don't accept gen-delims "[" in host) 127 | { 128 | "payid:alice$[example.com", // input 129 | IllegalArgumentException.class, // exception to expect 130 | "PayID 'host' for `payid:alice$[example.com` has an invalid value.",// message to expect 131 | }, 132 | //14 (don't accept gen-delims "]" in account) 133 | { 134 | "payid:]$example.com", // input 135 | IllegalArgumentException.class, // exception to expect 136 | "PayID 'account' for `payid:]$example.com` has an invalid value.",// message to expect 137 | }, 138 | //15 (don't accept gen-delims "]" in host) 139 | { 140 | "payid:alice$]example.com", // input 141 | IllegalArgumentException.class, // exception to expect 142 | "PayID 'host' for `payid:alice$]example.com` has an invalid value.",// message to expect 143 | }, 144 | //16 (don't accept gen-delims "@") 145 | { 146 | "payid:a$@example.com", // input 147 | IllegalArgumentException.class, // exception to expect 148 | "PayID 'host' for `payid:a$@example.com` has an invalid value.",// message to expect 149 | }, 150 | //17 (don't accept payid that start with a %-encoded value.) 151 | { 152 | "payid:%20abc$@example.com", // input 153 | IllegalArgumentException.class, // exception to expect 154 | "PayID `%20abc$@example.com` may not start with a percent-encoded value, but instead MUST start with " 155 | + "a character from either the 'unreserved' or 'sub-delims' set.", // message to expect 156 | }, 157 | //18 (don't accept payid that start with a %-encoded value.) 158 | { 159 | "payid:%20abc$example.com", // input 160 | IllegalArgumentException.class, // exception to expect 161 | "PayID `%20abc$example.com` may not start with a percent-encoded value, but instead MUST start with " 162 | + "a character from either the 'unreserved' or 'sub-delims' set.", // message to expect 163 | }, 164 | //19 (don't accept payid with a '^'.) 165 | { 166 | "payid:alice^$@example.com", // input 167 | IllegalArgumentException.class, // exception to expect 168 | "PayID 'account' for `payid:alice^$@example.com` has an invalid value.",// message to expect 169 | }, 170 | //20 (empty account) 171 | { 172 | "payid:$@example.com", // input 173 | IllegalStateException.class, // exception to expect 174 | "Cannot build PayId, some of required attributes are not set [account]",// message to expect 175 | }, 176 | //21 (empty account with path) 177 | { 178 | "payid:$example.com/bar", // input 179 | IllegalStateException.class, // exception to expect 180 | "Cannot build PayId, some of required attributes are not set [account]",// message to expect 181 | }, 182 | //22 (empty account with empty path) 183 | { 184 | "payid:$example.com/", // input 185 | IllegalStateException.class, // exception to expect 186 | "Cannot build PayId, some of required attributes are not set [account]",// message to expect 187 | }, 188 | //23 (empty account with double path) 189 | { 190 | "payid:$rafiki.money/p/test@example.com", // input 191 | IllegalStateException.class, // exception to expect 192 | "Cannot build PayId, some of required attributes are not set [account]",// message to expect 193 | }, 194 | //24 (no colon in acctpart) 195 | { 196 | "payid::alice$example.com", // input 197 | IllegalArgumentException.class, // exception to expect 198 | "PayID 'account' for `payid::alice$example.com` has an invalid value.", // message to expect 199 | } 200 | }); 201 | } 202 | 203 | @Test 204 | public void testInvalidValues() { 205 | expectedException.expect(exceptionClassToExpect); 206 | expectedException.expectMessage(exceptionMessageToExpect); 207 | 208 | PayId.of(sourcepayid); 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /java/src/test/java/org/payid/PayIdPublicSuffixesTest.java: -------------------------------------------------------------------------------- 1 | package org.payid; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 7 | import com.google.common.collect.ImmutableList; 8 | import com.google.common.collect.ImmutableList.Builder; 9 | import okhttp3.HttpUrl; 10 | import org.immutables.value.Value; 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | import org.junit.runners.Parameterized; 14 | import org.junit.runners.Parameterized.Parameters; 15 | 16 | import java.io.IOException; 17 | import java.net.MalformedURLException; 18 | import java.net.URISyntaxException; 19 | import java.net.URL; 20 | import java.util.Collection; 21 | import java.util.Objects; 22 | import java.util.Scanner; 23 | 24 | /** 25 | * Validates that all public suffixes work properly in a {@link PayId}. 26 | */ 27 | @RunWith(Parameterized.class) 28 | public class PayIdPublicSuffixesTest { 29 | 30 | private ValidPublicTldTestVector validPublicTldTestVector; 31 | 32 | public PayIdPublicSuffixesTest(final ValidPublicTldTestVector validPublicTldTestVector) { 33 | this.validPublicTldTestVector = Objects.requireNonNull(validPublicTldTestVector); 34 | } 35 | 36 | /** 37 | * The data for this test... 38 | */ 39 | @Parameters 40 | public static Collection data() throws URISyntaxException, MalformedURLException { 41 | final Scanner scanner; 42 | try { 43 | URL url = new URL("https://raw.githubusercontent.com/publicsuffix/list/master/public_suffix_list.dat"); 44 | scanner = new Scanner(url.openStream()); 45 | // read from your scanner 46 | } catch (IOException ex) { 47 | // there was some connection problem, or the file did not exist on the server, 48 | // or your URL was not in the right format. 49 | // think about what to do now, and put it here. 50 | throw new RuntimeException(ex.getMessage(), ex); 51 | } 52 | 53 | final Builder vectors = ImmutableList.builder(); 54 | while (scanner.hasNextLine()) { 55 | String line = scanner.nextLine(); 56 | if (line != null && line.trim().length() != 0 && !line.startsWith("//")) { 57 | String host = HttpUrl.parse("https://example." + line.trim()).host(); 58 | vectors.add( 59 | ValidPublicTldTestVector.builder() 60 | .tld(host) 61 | .expectedPayId("alice$" + line.trim()) 62 | .build() 63 | ); 64 | } 65 | } 66 | 67 | return vectors.build(); 68 | } 69 | 70 | @Test 71 | public void testPublicTlds() { 72 | final PayId payId = PayId.of("payid:alice$" + validPublicTldTestVector.tld()); 73 | 74 | assertThat(payId).isNotNull(); 75 | assertThat(payId.account()).isEqualTo("alice"); 76 | assertThat(payId.host()).isEqualTo(validPublicTldTestVector.tld()); 77 | assertThat(payId.toString()).isEqualTo("payid:alice$" + validPublicTldTestVector.tld()); 78 | } 79 | 80 | @Value.Immutable 81 | @JsonSerialize(as = ImmutableValidPublicTldTestVector.class) 82 | @JsonDeserialize(as = ImmutableValidPublicTldTestVector.class) 83 | public interface ValidPublicTldTestVector { 84 | 85 | static ImmutableValidPublicTldTestVector.Builder builder() { 86 | return ImmutableValidPublicTldTestVector.builder(); 87 | } 88 | 89 | String tld(); 90 | 91 | String expectedPayId(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /java/src/test/java/org/payid/PayIdValidValuesTest.java: -------------------------------------------------------------------------------- 1 | package org.payid; 2 | 3 | import org.junit.Rule; 4 | import org.junit.Test; 5 | import org.junit.rules.ExpectedException; 6 | import org.junit.runner.RunWith; 7 | import org.junit.runners.Parameterized; 8 | import org.junit.runners.Parameterized.Parameters; 9 | 10 | import java.util.Arrays; 11 | import java.util.Collection; 12 | import java.util.Objects; 13 | 14 | /** 15 | * Unit tests for {@link PayId}. 16 | */ 17 | @RunWith(Parameterized.class) 18 | public class PayIdInvalidValuesTest { 19 | 20 | @Rule 21 | public ExpectedException expectedException = ExpectedException.none(); 22 | 23 | private String sourcepayid; 24 | private Class exceptionClassToExpect; 25 | private String exceptionMessageToExpect; 26 | 27 | public PayIdInvalidValuesTest( 28 | final String sourcepayid, final Class exceptionToExpect, final String exceptionMessageToExpect 29 | ) { 30 | this.sourcepayid = sourcepayid; 31 | this.exceptionClassToExpect = Objects.requireNonNull(exceptionToExpect); 32 | this.exceptionMessageToExpect = exceptionMessageToExpect; 33 | } 34 | 35 | /** 36 | * The data for this test... 37 | */ 38 | @Parameters 39 | public static Collection data() { 40 | 41 | return Arrays.asList(new Object[][]{ 42 | 43 | ///////////////// 44 | // Invalid Values 45 | ///////////////// 46 | 47 | //0 48 | { 49 | null, // input 50 | NullPointerException.class, // exception to expect 51 | "PayID must not be null", // message to expect 52 | }, 53 | //1 54 | { 55 | "$", // input (missing host) 56 | IllegalArgumentException.class, // exception to expect 57 | "PayID `$` must start with the 'payid:' scheme", // message to expect 58 | }, 59 | //2 60 | { 61 | "payid:$", // input (missing host) 62 | IllegalArgumentException.class, // exception to expect 63 | "PayID `$` must specify a valid account and host", // message to expect 64 | }, 65 | //3 66 | { 67 | "alice$wallet.example", // input missing payid: prefix 68 | IllegalArgumentException.class, // exception to expect 69 | "PayID `alice$wallet.example` must start with the 'payid:' scheme", // message to expect 70 | }, 71 | //4 72 | { 73 | "payid:alice$nic.書籍", // input 74 | IllegalArgumentException.class, // exception to expect 75 | "PayID 'host' for `payid:alice$nic.書籍` has an invalid value.",// message to expect 76 | }, 77 | //5 (No IDNs). In order to work in a PayID, an IDN must be encoded properly using PUNY-Code or some other 78 | // mechanism that can translate into a value PayID. 79 | { 80 | "payid:書籍$example.com", // input 81 | IllegalArgumentException.class, // exception to expect 82 | "PayID 'account' for `payid:書籍$example.com` has an invalid value.",// message to expect 83 | }, 84 | //6 (don't accept gen-delims "?") 85 | { 86 | "payid:?$example.com", // input 87 | IllegalArgumentException.class, // exception to expect 88 | "PayID 'account' for `payid:?$example.com` has an invalid value.",// message to expect 89 | }, 90 | //7 (don't accept gen-delims "?") 91 | { 92 | "payid:a$?example.com", // input 93 | IllegalArgumentException.class, // exception to expect 94 | "PayID 'host' for `payid:a$?example.com` has an invalid value.",// message to expect 95 | }, 96 | //8 (don't accept gen-delims "#") 97 | { 98 | "payid:#$example.com", // input 99 | IllegalArgumentException.class, // exception to expect 100 | "PayID 'account' for `payid:#$example.com` has an invalid value.",// message to expect 101 | }, 102 | //9 (don't accept gen-delims "#") 103 | { 104 | "payid:a$#example.com", // input 105 | IllegalArgumentException.class, // exception to expect 106 | "PayID 'host' for `payid:a$#example.com` has an invalid value.",// message to expect 107 | }, 108 | //10 (don't accept gen-delims "[" in account) 109 | { 110 | "payid:[$example.com", // input 111 | IllegalArgumentException.class, // exception to expect 112 | "PayID 'account' for `payid:[$example.com` has an invalid value.",// message to expect 113 | }, 114 | //11 (don't accept gen-delims "[" in host) 115 | { 116 | "payid:alice$[example.com", // input 117 | IllegalArgumentException.class, // exception to expect 118 | "PayID 'host' for `payid:alice$[example.com` has an invalid value.",// message to expect 119 | }, 120 | //12 (don't accept gen-delims "]" in account) 121 | { 122 | "payid:]$example.com", // input 123 | IllegalArgumentException.class, // exception to expect 124 | "PayID 'account' for `payid:]$example.com` has an invalid value.",// message to expect 125 | }, 126 | //13 (don't accept gen-delims "]" in host) 127 | { 128 | "payid:alice$]example.com", // input 129 | IllegalArgumentException.class, // exception to expect 130 | "PayID 'host' for `payid:alice$]example.com` has an invalid value.",// message to expect 131 | }, 132 | //14 (don't accept gen-delims "@") 133 | { 134 | "payid:a$@example.com", // input 135 | IllegalArgumentException.class, // exception to expect 136 | "PayID 'host' for `payid:a$@example.com` has an invalid value.",// message to expect 137 | }, 138 | //15 (don't accept payid with a '^'.) 139 | { 140 | "payid:alice^$@example.com", // input 141 | IllegalArgumentException.class, // exception to expect 142 | "PayID 'account' for `payid:alice^$@example.com` has an invalid value.",// message to expect 143 | }, 144 | //16 (with path) 145 | { 146 | "payid:foo$example.com/bar", // input 147 | IllegalArgumentException.class, // exception to expect 148 | "PayID 'host' for `payid:foo$example.com/bar` has an invalid value.",// message to expect 149 | }, 150 | //17 (with empty path) 151 | { 152 | "payid:foo$example.com/", // input 153 | IllegalArgumentException.class, // exception to expect 154 | "PayID 'host' for `payid:foo$example.com/` has an invalid value.",// message to expect 155 | }, 156 | //18 (with double path) 157 | { 158 | "payid:foo$rafiki.money/p/test@example.com", // input 159 | IllegalArgumentException.class, // exception to expect 160 | "PayID 'host' for `payid:foo$rafiki.money/p/test@example.com` has an invalid value.", // message to expect 161 | }, 162 | }); 163 | } 164 | 165 | @Test 166 | public void testInvalidValues() { 167 | expectedException.expect(exceptionClassToExpect); 168 | expectedException.expectMessage(exceptionMessageToExpect); 169 | 170 | PayId.of(sourcepayid); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /java/src/test/resources/org/payid/valid-payids.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "0. PayID Happy path", 4 | "payIdInput": "payid:alice$example.com", 5 | "expectedAccountId": "alice", 6 | "expectedHost": "example.com", 7 | "expectedStringValue": "payid:alice$example.com" 8 | }, 9 | { 10 | "description": "1. `unreserved` account characters", 11 | "payIdInput": "payid:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~$example.net", 12 | "expectedAccountId": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789-._~", 13 | "expectedHost": "example.net", 14 | "expectedStringValue": "payid:abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789-._~$example.net" 15 | }, 16 | { 17 | "description": "2. (sub-delims)", 18 | "payIdInput": "payid:!$&'()*+,;=$example.net", 19 | "expectedAccountId": "!$&'()*+,;=", 20 | "expectedHost": "example.net", 21 | "expectedStringValue": "payid:!$&'()*+,;=$example.net" 22 | }, 23 | { 24 | "description": "3. (sub-delims first, then unreserved)", 25 | "payIdInput": "payid:!alice$example.net", 26 | "expectedAccountId": "!alice", 27 | "expectedHost": "example.net", 28 | "expectedStringValue": "payid:!alice$example.net" 29 | }, 30 | { 31 | "description": "4. (unreserved first, then sub-delims)", 32 | "payIdInput": "payid:aliCE$!example.net", 33 | "expectedAccountId": "alice", 34 | "expectedHost": "!example.net", 35 | "expectedStringValue": "payid:alice$!example.net" 36 | }, 37 | { 38 | "description": "5. (percent-encoded $)", 39 | "payIdInput": "payid:alice%24wallet.example$bank.example.net", 40 | "expectedAccountId": "alice%24wallet.example", 41 | "expectedHost": "bank.example.net", 42 | "expectedStringValue": "payid:alice%24wallet.example$bank.example.net" 43 | }, 44 | { 45 | "description": "6 (encoded IDN)", 46 | "payIdInput": "payid:alice$nic.xn--rovu88b", 47 | "expectedAccountId": "alice", 48 | "expectedHost": "nic.xn--rovu88b", 49 | "expectedStringValue": "payid:alice$nic.xn--rovu88b" 50 | }, 51 | { 52 | "description": "7 (host with port)", 53 | "payIdInput": "payid:alice$example.com:8080", 54 | "expectedAccountId": "alice", 55 | "expectedHost": "example.com:8080", 56 | "expectedStringValue": "payid:alice$example.com:8080" 57 | }, 58 | { 59 | "description": "8. (Capitalized PAYID)", 60 | "payIdInput": "PAYID:alice$example.com:8080", 61 | "expectedAccountId": "alice", 62 | "expectedHost": "example.com:8080", 63 | "expectedStringValue": "payid:alice$example.com:8080" 64 | }, 65 | { 66 | "description": "9. (accept \":\" in host)", 67 | "payIdInput": "payid:alice$:example.com", 68 | "expectedAccountId": "alice", 69 | "expectedHost": ":example.com", 70 | "expectedStringValue": "payid:alice$:example.com" 71 | }, 72 | { 73 | "description": "10. Double-embedded dollar-signs", 74 | "payIdInput": "payid:alice$foo.example.com$bar.example.com", 75 | "expectedAccountId": "alice$foo.example.com", 76 | "expectedHost": "bar.example.com", 77 | "expectedStringValue": "payid:alice$foo.example.com$bar.example.com" 78 | }, 79 | { 80 | "description": "11. Triple-embedded dollar-signs", 81 | "payIdInput": "payid:alice$foo.example.com$bar.example.com$baz.example.com", 82 | "expectedAccountId": "alice$foo.example.com$bar.example.com", 83 | "expectedHost": "baz.example.com", 84 | "expectedStringValue": "payid:alice$foo.example.com$bar.example.com$baz.example.com" 85 | }, 86 | { 87 | "description": "12. Percent-Encoded Email address", 88 | "payIdInput": "payid:alice%40example.com$example.com", 89 | "expectedAccountId": "alice%40example.com", 90 | "expectedHost": "example.com", 91 | "expectedStringValue": "payid:alice%40example.com$example.com" 92 | }, 93 | { 94 | "description": "13. Percent-Encoded Carat", 95 | "payIdInput": "payid:alice%5E$example.com", 96 | "expectedAccountId": "alice%5E", 97 | "expectedHost": "example.com", 98 | "expectedStringValue": "payid:alice%5E$example.com" 99 | }, 100 | { 101 | "description": "14. path-abempty (begins with '/' or is empty)", 102 | "payIdInput": "payid:/$example.com", 103 | "expectedAccountId": "/", 104 | "expectedHost": "example.com", 105 | "expectedStringValue": "payid:/$example.com" 106 | }, 107 | { 108 | "description": "15. path-abempty (begins with '/' or is empty)", 109 | "payIdInput": "payid:$example.com", 110 | "expectedAccountId": "", 111 | "expectedHost": "example.com", 112 | "expectedStringValue": "payid:$example.com" 113 | }, 114 | { 115 | "description": "16. path-absolute (1) begins with '/' and with at least 1 char", 116 | "payIdInput": "payid:/t$example.com", 117 | "expectedAccountId": "/t", 118 | "expectedHost": "example.com", 119 | "expectedStringValue": "payid:/t$example.com" 120 | }, 121 | { 122 | "description": "17. path-absolute (2) begins with '/' and with at least 1 char", 123 | "payIdInput": "payid:/test/user$example.com", 124 | "expectedAccountId": "/test/user", 125 | "expectedHost": "example.com", 126 | "expectedStringValue": "payid:/test/user$example.com" 127 | }, 128 | { 129 | "description": "18. path-absolute (3) begins with '/' and with at least 1 char", 130 | "payIdInput": "payid:test/user/$example.com", 131 | "expectedAccountId": "test/user/", 132 | "expectedHost": "example.com", 133 | "expectedStringValue": "payid:test/user/$example.com" 134 | }, 135 | { 136 | "description": "19. path-absolute (4) begins with '/' and with at least 1 char", 137 | "payIdInput": "payid:/test/user/$example.com", 138 | "expectedAccountId": "/test/user/", 139 | "expectedHost": "example.com", 140 | "expectedStringValue": "payid:/test/user/$example.com" 141 | }, 142 | { 143 | "description": "20. path-noscheme (begins with a non-colon segment)", 144 | "payIdInput": "payid:f$example.com", 145 | "expectedAccountId": "f", 146 | "expectedHost": "example.com", 147 | "expectedStringValue": "payid:f$example.com" 148 | }, 149 | { 150 | "description": "21. path-rootless (allows a colon to start)", 151 | "payIdInput": "payid::$example.com", 152 | "expectedAccountId": ":", 153 | "expectedHost": "example.com", 154 | "expectedStringValue": "payid::$example.com" 155 | }, 156 | { 157 | "description": "22. Allowed chars", 158 | "payIdInput": "payid:foo$fa$t.com", 159 | "expectedAccountId": "foo$fa", 160 | "expectedHost": "t.com", 161 | "expectedStringValue": "payid:foo$fa$t.com" 162 | } 163 | ] 164 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "payid-rfcs", 3 | "version": "1.0.0", 4 | "description": "PayID RFCs and associated tests", 5 | "keywords": [ 6 | "payid", 7 | "spec", 8 | "test", 9 | "vectors", 10 | "testsuite", 11 | "suite", 12 | "unit", 13 | "cases", 14 | "crypto", 15 | "verify" 16 | ], 17 | "homepage": "https://github.com/payid-org/rfcs#readme", 18 | "bugs": { 19 | "url": "https://github.com/payid-org/rfcs/issues" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "git+https://github.com/payid-org/rfcs.git" 24 | }, 25 | "license": "Apache-2.0", 26 | "author": "David Fuelling ", 27 | "main": "index.js", 28 | "scripts": { 29 | "spec": "rm -f dist/spec/payid.*; grunt kramdown_rfc2629", 30 | "watch": "npm run spec && grunt watch", 31 | "test": "echo \"Error: no test specified\" && exit 1" 32 | }, 33 | "dependencies": { 34 | "grunt-cli": "^1.3.2" 35 | }, 36 | "devDependencies": { 37 | "grunt": "^1.0.1", 38 | "grunt-contrib-watch": "^1.0.0", 39 | "grunt-kramdown-rfc2629": "0.0.2" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/spec/payid-discovery.md: -------------------------------------------------------------------------------- 1 | --- 2 | coding: utf-8 3 | 4 | title: PayID Discovery 5 | docname: draft-fuelling-payid-discovery-01 6 | category: std 7 | 8 | pi: [toc, sortrefs, symrefs, comments] 9 | smart_quotes: off 10 | 11 | area: security 12 | author: 13 | 14 | - 15 | ins: D. Fuelling 16 | name: David Fuelling 17 | org: Ripple 18 | street: 315 Montgomery Street 19 | city: San Francisco 20 | region: CA 21 | code: 94104 22 | country: US 23 | 24 | normative: 25 | RFC2119: 26 | RFC3986: 27 | RFC4627: 28 | RFC6570: 29 | RFC7033: 30 | RFC7230: 31 | PAYID-URI: 32 | title: "The 'payid' URI Scheme" 33 | target: https://tbd.example.com/ 34 | author: 35 | ins: D. Fuelling 36 | fullname: David Fuelling 37 | 38 | informative: 39 | RFC5988: 40 | RFC6415: 41 | 42 | --- note_Feedback 43 | 44 | This specification is a draft proposal, and is part of the 45 | [PayID Protocol](https://payid.org/) initiative. Feedback related to this 46 | document should be sent in the form of a Github issue at: 47 | https://github.com/payid-org/rfcs/issues. 48 | 49 | --- abstract 50 | This specification defines the PayID Discovery protocol, which can be used 51 | to discover information about a 'payid' URI using standard HTTP methods. 52 | 53 | The primary use-case of this protocol is to define how to transform a 54 | PayID URI into a URL that can be used with other protocols. 55 | 56 | --- middle 57 | 58 | # Introduction 59 | PayID Discovery is used to transform a PayID URI [PAYID-URI][] into a 60 | URL (defined below as a PayID Discovery URL) that can then be used by 61 | higher-order protocols to discover metadata about a PayID-enabled service 62 | provider. 63 | 64 | This document specifies two modes of PayID discovery: one using 65 | Webfinger [RFC7033][] to resolve a corresponding PayID Discovery URL 66 | from a PayID using an interactive protocol. The second mode uses a manual 67 | mechanism to assemble a PayID Discovery URL from a PayID by-hand. 68 | 69 | In 'interactive' mode, a PayID can be presented to a Webfinger-enabled 70 | service endpoint that supports PayID Discovery. The resource returns a 71 | Webfinger-compliant JavaScript Object Notation (JSON) [RFC4627][] object 72 | that can be used to perform PayID Discovery as defined in section 4.1 of 73 | this document. 74 | 75 | As an alternative, "manual" mode MAY be used to decompose a PayID into a 76 | URL, without any intermediate server interaction by simply transposing 77 | portions of a PayID URI into a URL format. This procedure is defined in 78 | section 4.2 of this document. 79 | 80 | It should be noted that "manual" mode does not allow divergence between the 81 | string characters in a PayID URI and any corresponding PayID URL. 82 | Interactive mode, on the other hand, does allow such divergence, and is 83 | thus more powerful. For example, in manual mode, the PayID 84 | 'alice$example.com' MUST always map to the URL 85 | 'https://example.com/alice', whereas in interactive mode that same 86 | PayID URI can map to any arbitrary URL structure determined by the 87 | service provider, such as 'https://example.com/users/alice'. 88 | 89 | Information returned via PayID Discovery might be used for direct human 90 | consumption (e.g., looking up someone's Bitcoin address), or it might be 91 | used by systems to help carry out some operation (e.g., facilitating, 92 | with additional security mechanisms, protocols to support compliance or 93 | other legal requirements necessary to facilitate a payment). 94 | 95 | The information returned via this protocol is intended to be static 96 | in nature. As such, PayID Discovery is not intended to be used to return 97 | dynamic information like a payment account balance or the current 98 | status of a payment account. 99 | 100 | PayID Discovery is designed to be used across many applications. Use of 101 | PayID Discovery is illustrated in the examples in Section 3 and 102 | described more formally in Section 4. 103 | 104 | # Terminology 105 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 106 | "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and 107 | "OPTIONAL" in this document are to be interpreted as described in 108 | [RFC2119][]. 109 | 110 | # Example Usage 111 | This section shows sample uses of PayID Discovery in several 112 | hypothetical scenarios. 113 | 114 | ## PayID Discovery by a Wallet 115 | Imagine Alice wishes to send a friend some XRP from a web-based wallet 116 | provider that Alice has an account on. Alice would log-in to the wallet 117 | provider and enter Bob's PayID (say, `bob$receiver.example.com`) into the 118 | wallet UI to start the payment. 119 | 120 | The Wallet application would first perform a WebFinger query looking for 121 | the PayID Discovery service provider, like this: 122 | 123 | GET /.well-known/webfinger? 124 | resource=payid%3Abob%24receiver.example.com 125 | HTTP/1.1 126 | Host: receiver.example.com 127 | 128 | The server might respond like this: 129 | 130 | HTTP/1.1 200 OK 131 | Access-Control-Allow-Origin: * 132 | Content-Type: application/jrd+json 133 | 134 | { 135 | "subject" : "payid:bob$receiver.example.com", 136 | "links" : 137 | [ 138 | { 139 | "rel": "https://payid.org/ns/payid-uri-template/1.0", 140 | "template": "https://receiver.example.com/users/{acctpart}" 141 | } 142 | ] 143 | } 144 | 145 | Alice's wallet then uses the URL template found in the `template` property 146 | to assemble the specified PayId URL, 147 | `https://receiver.example.com/users/bob`. 148 | 149 | Per [RFC7033][], Webfinger requests can be filtered by using a "rel" 150 | parameter in the Webfinger request. Because support for the "rel" parameter 151 | is not required nor guaranteed, the client must not assume the "links" 152 | array will contain only the link relations related to PayID Discovery. 153 | 154 | ## PayID Discovery with Default Template 155 | Imagine Alice, as in the example above, wants to send a friend some XRP 156 | from a web-based wallet provider that Alice has an account on. However, in 157 | this example, let's assume that the PayID Alice is wanting to pay doesn't 158 | support "interactive" PayID discovery (i.e., the receiver's server doesn't 159 | support Webfinger). 160 | 161 | Alice would log-in to her wallet provider and enter Bob's PayID (say 162 | `bob$receiver.example.com`) to make a payment. 163 | 164 | The Wallet application would first attempt a WebFinger query as in the 165 | example above, like this: 166 | 167 | GET /.well-known/webfinger? 168 | resource=payid%3Abob%24receiver.example.com& 169 | HTTP/1.1 170 | Host: receiver.example.com 171 | 172 | However, in this case the `receiver.example.com` server doesn't support 173 | "interactive" PayID Discovery, so the server responds like this: 174 | 175 | HTTP/1.1 404 NOT FOUND 176 | 177 | Because Alice's Wallet can utilize "manual" PayID Discovery, the wallet 178 | software merely transforms `bob$receiever.example.com` into the URL 179 | `https://receiver.example.com/bob`. Alice's wallet then uses that URL to 180 | continue making a PayID payment. 181 | 182 | It should be noted that "manual" mode does not allow the PayID URI to 183 | diverge from the underlying URL returned via PayID Discovery. Because of 184 | this, "interactive" PayID Discovery is generally preferred. 185 | 186 | # PayID Discovery Protocol 187 | The PayID Discovery protocol is used to request information about an entity 188 | identified by a PayID URI. 189 | 190 | When successful, PayID Discovery always yields a PayID URL, which is a 191 | URI as defined by [RFC3986][] using the 'https' scheme defined in section 192 | 2.7.2 [RFC7230][]. A PayID URL can be used for any purposes outside the 193 | scope of this document. 194 | 195 | PayID Discovery is performed using one of two modes: "interactive" or 196 | "manual." Clients MUST attempt "interactive" mode first. If that 197 | mode fails to yield a PayID URL, then "manual" mode MAY be used as an 198 | alternative discovery mechanism. 199 | 200 | ## Interactive Mode 201 | Interactive PayID Discovery is broken up into a series of steps, each of 202 | which is defined in more detail below. The following is a visual 203 | representation of the protocol flow: 204 | 205 | +--------------------------+ 206 | | PayID URI | 207 | | alice$example.com | 208 | +--------------------------+ 209 | | 210 | v 211 | +--------------------------+ 212 | | Assemble | 213 | | PayID Discovery URL | 214 | +--------------------------+ 215 | | 216 | v 217 | +--------------------------+ 218 | | Query | 219 | +-----| PayID Discovery URL |-----+ 220 | | +--------------------------+ | 221 | | Success 222 | | | 223 | | v 224 | | +---------------------------+ 225 | Failure | Parse PayID Metadata | 226 | | +---------------------------+ 227 | | | 228 | | v 229 | | +---------------------------+ 230 | | | Select PayID URI Template | 231 | | +---------------------------+ 232 | | | 233 | v v 234 | +--------------------------+ +---------------------------+ 235 | | Manual PayID Discovery | | Assemble PayID URL | 236 | +--------------------------+ +---------------------------+ 237 | | | 238 | +---------------------+----------------+ 239 | | 240 | v 241 | +---------------------------+ 242 | | PayID URL | 243 | | https://example.com/alice | 244 | +---------------------------+ 245 | 246 | ### Step 1: Assemble PayID Discovery URL 247 | PayID Discovery utilizes the Webfinger [RFC7033][] specification in a 248 | narrowly defined profile. 249 | 250 | This document defines a PayID Discovery URL as being a Webfinger 251 | resource URI where the specified resource value is a valid PayID 252 | URI [PAYID-URI][]. 253 | 254 | For example, the PayID Discovery URL for alice$example.com is 255 | 256 | https://example.com/.well-known/webfinger? 257 | resource=payid%3Aalice%24example.com 258 | 259 | ### Step 2: Query PayID Discovery URL 260 | A Webfinger query MUST be performed against the PayID Discovery URL, 261 | as described in section 4.2 of Webfinger. 262 | 263 | In response, the WebFinger resource returns a JSON Resource Descriptor (JRD) 264 | as the resource representation to convey information about the requested 265 | PayID. 266 | 267 | If the Webfinger endpoint returns a non-200 HTTP response status code, then 268 | interactive PayID Discovery is considered to have failed. Clients MAY 269 | attempt to assemble a PayID URL using "manual" mode as defined in section 270 | 4.2.1 of this document. 271 | 272 | ### Step 3: Parse PayID Metadata 273 | If the PayID Discovery server returns a valid response, the response will 274 | contain one or more of the JRDs defined in section 5 of this document. 275 | 276 | If any of the JRDs contain a 'rel' value that represents a PayID URL 277 | Template, then that template value MUST be used in the next protocol step. 278 | 279 | Failing the above, if the 'rel' value of any JRDs represents a PayID 280 | Discovery URL, then that URL MUST be used in step 2 above, repeated 281 | recursively if needed, until a valid PayID URI Template is obtained. 282 | That URI Template value MUST be used in the next protocol step. 283 | 284 | ### Step 4: Assemble PayID URL 285 | A PayID URL is constructed by applying the PayID URI to the PayID URI 286 | Template string obtained in the step above. The PayID URI template MAY 287 | contain a URI string without any variables to represent a host-level 288 | PayID URL that is identical for every PayID URI on a particular host. 289 | 290 | For example, a PayID Discovery endpoint that only supports a single account 291 | might use a URI template string with no variables, like this: 292 | 293 | { 294 | "rel": "https://payid.org/ns/payid-uri-template/1.0", 295 | "template": "https://example.com/alice" 296 | } 297 | 298 | The result of this step is the PayID URL. Once obtained, PayID Discovery 299 | is considered to have completed successfully. 300 | 301 | #### Template Syntax 302 | This specification defines a simple template syntax for PayID URI 303 | transformation. A template is a string containing brace-enclosed 304 | ("{}") variable names marking the parts of the string that are to be 305 | substituted by the corresponding variable values. 306 | 307 | This specification defines a one variable -- "acctpart" -- which 308 | corresponds to the 'acctpart' of a PayID URI as defined in [PAYID-URI][]. 309 | 310 | When substituting the 'acctpart' value into a URI 'path' as defined by 311 | [RFC3986][], values MUST NOT be percent or otherwise encoded because the 312 | 'acctpart' value of a PayID URI always conforms to the character set 313 | allowed by paths in [RFC3986][]. 314 | 315 | However, before substituting template variables into a URI 'query' part, 316 | values MUST be encoded using UTF-8, and any character other than 317 | unreserved (as defined by [RFC3986]) MUST be percent-encoded per [RFC3986]. 318 | 319 | Protocols MAY define additional variables and syntax rules, but MUST NOT 320 | change the meaning of the 'acctpart' variable. If a client is unable to 321 | successfully process a template (e.g., unknown variable names, unknown or 322 | incompatible syntax), the JRD SHOULD be ignored. 323 | 324 | The template syntax ABNF is as follows: 325 | 326 | uri-char = ( reserved / unreserved / pct-encoded ) 327 | var-char = ALPHA / DIGIT / "." / "_" 328 | var-name = %x61.63.63.74.70.61.72.74 / ( 1*var-char ) ; "acctpart" or 329 | other names 330 | variable = "{" var-name "}" 331 | PAYID-URI-Template = *( uri-char / variable ) 332 | 333 | For example: 334 | 335 | Input: alice$example.org 336 | Template: https://example.org/{acctpart} 337 | Output: https://example.org/alice 338 | 339 | ## Fallback Mode 340 | If "Interactive" mode is not supported or otherwise fails to yield a PayID 341 | URL, then a PayID URL MAY be assembled manually using the following 342 | predefined ruleset: 343 | 344 | 1. Decompose the PayID URI into its component parts, per [PAYID-URI][], 345 | capturing the 'acctpart' and 'host' values. 346 | 347 | 1. Using the 'acctpart' and 'host', assemble a URL by substituting each 348 | value into the following string using no special encoding or other 349 | character adjustments: `https://{host}/{acctpart}`. 350 | 351 | For example: 352 | 353 | Input: bob.primary$example.org 354 | Output: https://example.org/bob.primary 355 | 356 | The resulting URL is a PayID URL. 357 | 358 | ### Fallback Assembly Flow 359 | The following is a visual representation of the Fallback Assembly protocol 360 | flow: 361 | 362 | +--------------------------+ 363 | | PayID URI | 364 | | alice$example.com | 365 | +--------------------------+ 366 | | 367 | v 368 | +--------------------------+ 369 | |Manual PayID URL Assembly | 370 | +--------------------------+ 371 | | 372 | v 373 | +---------------------------+ 374 | | PayID URL | 375 | | https://example.com/alice | 376 | +---------------------------+ 377 | 378 | # PayID Discovery JRDs 379 | This document defines two JRDs that conform to section 4.4 of the 380 | Webfinger RFC. 381 | 382 | ## JRD for PayID Discovery URL 383 | This type of JRD can be used to represent a URL that is a PayID Discovery 384 | URL. This is useful for delegating PayID Discovery to another service 385 | endpoint: 386 | 387 | * 'rel': `https://payid.org/ns/payid-discovery-url/1.0` 388 | * 'href': A PayID Discovery URL that clients can dereference to perform 389 | interactive PayID Discovery. 390 | 391 | The following is an example of a JRD that indicates a PayID Discovery URL: 392 | 393 | { 394 | "rel": "https://payid.org/ns/payid-discovery-url/1.0", 395 | "href": "https://delegate.example.com/.well-known/webfinger?resource= 396 | payid%3Aalice%24example.com" 397 | } 398 | 399 | ## JRD for PayID URI Template 400 | This type of JRD can be used to represent a URL that is a PayID URL 401 | Template. 402 | 403 | * 'rel': `https://payid.org/ns/payid-uri-template/1.0` 404 | * 'template': A PayID URI Template 405 | 406 | The following is an example of a JRD that indicates a PayID URI Template: 407 | 408 | { 409 | "rel": "https://payid.org/ns/payid-uri-template/1.0", 410 | "template": "https://example.com/{acctpart}" 411 | } 412 | 413 | # Security Considerations 414 | Various security considerations should be taken into account for PayID 415 | Discovery. 416 | 417 | Among other resource, consult section 9 of [RFC7033][] and section 7 of 418 | [RFC3986][] for important security considerations involved in PayID 419 | Discovery. 420 | 421 | ## Hosted PayID Discovery Services 422 | As with most services provided on the Internet, it is possible for a domain 423 | owner to utilize "hosted" WebFinger services. Consult section 7 of 424 | [RFC7033][] for considerations that could apply to both "manual" and 425 | "interactive" PayID Discovery when hosted by a third-party. 426 | 427 | ## Cross-Origin Resource Sharing (CORS) 428 | PayID Discovery resources might not be accessible from a web browser due to 429 | "Same-Origin" policies. See section 5 of [RFC7033][] for CORS considerations 430 | that apply to both "manual" and "interactive" PayID Discovery modes. 431 | 432 | ## Access Control 433 | As with all web resources, access to the PayID Discovery resource could 434 | require authentication. See section 6 of [RFC7033][] for Access Control 435 | considerations that could apply to both "manual" and "interactive" PayID 436 | Discovery modes. 437 | 438 | # IANA Considerations 439 | 440 | ## New Link Relation Types 441 | This document defines the following Link relation types per [RFC7033][]. 442 | See section 3 for examples of each type of Link. 443 | 444 | ### PayID Discovery URL 445 | 446 | * Relation Type ('rel'): `https://payid.org/ns/payid-discovery-url/1.0` 447 | * Media Type: `application/jrd+json` 448 | * Description: PayID Discovery URL, version 1.0 449 | 450 | ### PayID Discovery URI Template 451 | 452 | * Relation Type ('rel'): `https://payid.org/ns/payid-uri-template/1.0` 453 | * Media Type: `application/jrd+json` 454 | * Description: PayID Discovery URI Template, version 1.0 455 | 456 | # Acknowledgments 457 | This document was heavily influenced by, and builds upon, Webfinger 458 | [RFC7033][] (adapted for a payments use-case) as well as the supporting 459 | RFCs that it relies upon and that influenced it, especially [RFC5988][] and 460 | [RFC6415][]. The author would like to acknowledge the contributions of 461 | everyone who worked on those and any related specifications. 462 | 463 | In addition, the author would like to acknowledge everyone who provided 464 | feedback and use-cases for this derivative specification. 465 | -------------------------------------------------------------------------------- /src/spec/payid-easy-checkout.md: -------------------------------------------------------------------------------- 1 | --- 2 | coding: utf-8 3 | 4 | title: Draft 1 - PayID Easy Checkout Protocol 5 | docname: payid-easy-checkout-protocol 6 | category: std 7 | 8 | pi: [toc, sortrefs, symrefs, comments] 9 | smart_quotes: off 10 | 11 | area: security 12 | author: 13 | - 14 | ins: N. Kramer 15 | name: Noah Kramer 16 | org: Ripple 17 | street: 315 Montgomery Street 18 | city: San Francisco 19 | region: CA 20 | code: 94104 21 | country: US 22 | phone: ----------------- 23 | email: nkramer@ripple.com 24 | uri: https://www.ripple.com 25 | - 26 | ins: D. Fuelling 27 | name: David Fuelling 28 | org: Ripple 29 | street: 315 Montgomery Street 30 | city: San Francisco 31 | region: CA 32 | code: 94104 33 | country: US 34 | phone: ----------------- 35 | email: fuelling@ripple.com 36 | uri: https://www.ripple.com 37 | - 38 | ins: I. Simpson 39 | name: Ian Simpson 40 | org: Ripple 41 | street: 315 Montgomery Street 42 | city: San Francisco 43 | region: CA 44 | code: 94104 45 | country: US 46 | phone: ----------------- 47 | email: isimpson@ripple.com 48 | uri: https://www.ripple.com 49 | 50 | normative: 51 | RFC2119: 52 | RFC2818: 53 | RFC3986: 54 | RFC6265: 55 | RFC7033: 56 | RFC7231: 57 | RFC7413: 58 | RFC6570: 59 | RFC8446: 60 | PAYID-PROTOCOL: 61 | title: "PayID Protocol" 62 | author: 63 | ins: A. Malhotra 64 | fullname: Aanchal Malhotra 65 | ins: D. Schwartz 66 | fullname: David Schwartz 67 | PAYID-URI: 68 | title: "The 'payid' URI Scheme" 69 | target: https://tbd.example.com/ 70 | author: 71 | ins: D. Fuelling 72 | fullname: David Fuelling 73 | PAYID-DISCOVERY: 74 | title: "PayID Discovery" 75 | author: 76 | ins: D. Fuelling 77 | fullname: David Fuelling 78 | 79 | informative: 80 | RFC5988: 81 | 82 | --- note_Feedback 83 | 84 | This specification is a draft proposal, and is part of the [PayID Protocol](https://payid.org/) initiative. Feedback related to this document should be sent in the form of a Github issue at: https://github.com/payid-org/rfcs/issues. 85 | 86 | --- abstract 87 | This specification formalizes how a payment recipient, such as a merchant or a non-profit, can automatically 88 | initiate a payment from a sender using only the sender's PayID. 89 | 90 | --- middle 91 | 92 | # Terminology 93 | 94 | This protocol can be referred to as the `PayID Easy Checkout Protocol`. It uses the following terminology: 95 | 96 | * PayID Easy Checkout Client: A client that assembles a PayID Easy Checkout URL using information obtained from PayID Discovery Server via [PAYID-DISCOVERY][]. 97 | * PayID Discovery Server: An endpoint that returns a PayID Discovery JRD conforming to [PAYID-DISCOVERY][]. 98 | * Recipient: An individual or entity receiving a payment (e.g., e-commerce merchant, charity). 99 | * Sender: An individual or entity originating a payment to a `recipient`. 100 | * Wallet: A device or application that holds funds (may be a non-custodial wallet). 101 | * PayID Easy Checkout URL: A URL that is the result of this protocol; can be used to redirect a client to a wallet corresponding to a particular PayID as defined in [PAYID-URI][]. 102 | 103 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119][] and [RFC9174][]. 104 | 105 | # Introduction 106 | 107 | The PayID Easy Checkout Protocol allows a recipient (e.g., an online merchant or a charity) to 108 | request a payment from a sender using only the sender's [PayID][PAYID-URI]. Implementations 109 | of this protocol require little to no server-side engineering effort, while creating a seamless and uniform 110 | user experience for senders. 111 | 112 | The main focus of the protocol is on PayID Easy Checkout Discovery, which defines how a PayID Easy Checkout Client can 113 | retrieve a PayID Easy Checkout URL and use it to initiate 114 | a payment to the merchant. 115 | 116 | Though the [appendix](#appendix) of this specification provides an example usage of 117 | this protocol using Web Redirects, supplemental RFCs are needed to define any different ways in which a PayID 118 | client can utilize a PayID Easy Checkout URL. 119 | 120 | # PayID Easy Checkout Protocol 121 | The PayID Easy Checkout Protocol can be used to initiate an end-to-end checkout flow between a payment recipient, such 122 | as an online merchant, and a sender. 123 | 124 | The protocol is comprised of two parts: 125 | 126 | 1. PayID Easy Checkout Discovery 127 | 2. PayID Easy Checkout URL Assembly 128 | 129 | ## PayID Easy Checkout Discovery 130 | PayID Easy Checkout Discovery extends [PAYID-DISCOVERY][] by defining a new link relation in the PayID metadata JRD 131 | returned by a PayID Discovery query. This link relation, defined in the [JRD](#payid-easy-checkout-jrds) section 132 | of this specification, includes the URL on the sender's wallet that can 133 | be used to initiate a payment. 134 | 135 | Recipients who wish to initiate an Easy Checkout flow MUST first query the sender's PayID Discovery Server to 136 | obtain a PayID Easy Checkout URL. Therefore, PayID Discovery Servers that wish to enable PayID Easy 137 | Checkout MUST include an Easy Checkout JRD Link 138 | in all PayID Easy Checkout Discovery responses. 139 | 140 | Recipients SHOULD implement fallback measures to complete a checkout flow if a sender's wallet does not support PayID Easy Checkout. 141 | 142 | The following steps describe how a PayID Easy Checkout Client can query a PayID Discovery Server to obtain a PayID Easy Checkout URL. 143 | 144 | ### Step 1: Assemble PayID Easy Checkout Discovery URL 145 | The process of assembling a PayID Discovery URL is defined in section 4.1.1 of [PAYID-DISCOVERY][]. 146 | 147 | ### Step 2: Query PayID Easy Checkout Discovery URL 148 | The process of querying the PayID Discovery URL is defined in section 4.1.2 of [PAYID-DISCOVERY][]. 149 | 150 | Clients SHOULD implement fallback measures to complete checkout if the PayID Easy Checkout Discovery query fails. 151 | 152 | ### Step 3: Parse PayID Easy Checkout Metadata 153 | If PayID Easy Checkout is supported, a PayID Discovery server MUST respond to discovery requests with an HTTP status code of `200` and a JSON payload 154 | containing a JRD with an Easy Checkout link relation. 155 | 156 | For example, a PayID Discovery Server might respond to a PayID Discovery query with the following payload: 157 | 158 | { 159 | "subject": "payid:alice$wallet.com", 160 | "links": [ 161 | { 162 | "rel" : "https://payid.org/ns/payid-easy-checkout-uri/1.0", 163 | "href": "https://wallet.com/checkout" 164 | } 165 | ] 166 | } 167 | 168 | A PayID Easy Checkout client MUST parse this response to find the PayID Easy Checkout Link. 169 | If the JRD returned from the PayID Discovery query does not contain a 170 | PayID Easy Checkout Link in its 'links' collection, PayID Easy Checkout is considered to have failed. 171 | 172 | However, if a PayID Easy Checkout URL can been obtained from the PayID Easy Checkout Link, 173 | PayID Easy Checkout Discovery is considered to be complete. 174 | 175 | ## PayID Easy Checkout URL Assembly 176 | A PayID Easy Checkout URL represents the resource on a wallet that can 177 | be used by a sender to complete a payment. However, before directing a sender to their wallet, the recipient 178 | MUST append all of the query parameters defined in the [following section](#payid-easy-checkout-url-query-parameters). 179 | 180 | Once a PayID Easy Checkout URL is assembled, PayID Easy Checkout is considered to be complete. 181 | 182 | ### PayID Easy Checkout URL Query Parameters 183 | This specification defines several query parameter names and corresponding datatypes which MUST be added to the 184 | PayID Easy Checkout URL before redirecting a sender to their wallet. The PayID Easy Checkout URL SHOULD be parsed 185 | by the wallet in order to retrieve any values set by the recipient. It is RECOMMENDED that wallets use these 186 | values to pre-populate a payment transaction. 187 | 188 | | Name | Type | Description | 189 | |----------------|------------------|----------------------------------------------------------------------| 190 | | amount | integer | The amount that should be sent by the sender to the recipient. | 191 | | receiverPayId | string | The [PAYID-URI][] of the receiver. | 192 | | assetCode | string | The currency code that denominates the amount as defined in [PAYID-PROTOCOL][].| 193 | | assetScale | short | Defines how many units make up one regular unit of the assetCode. | 194 | | paymentNetwork | string | The payment network, as defined in [PAYID-PROTOCOL][], that the sender should use to send a payment. | 195 | | nextUrl | HTTP Url string | A URL that the sender's wallet can navigate a sender to after the sender completes a payment. | 196 | |----------------|------------------|----------------------------------------------------------------------| 197 | 198 | When adding values into a URI 'query' part as defined by 199 | [RFC3986][], values with characters outside the character set allowed by query parameters in [RFC3986][] 200 | MUST be percent or otherwise encoded. 201 | 202 | Protocols MAY define additional query parameter names and syntax rules, but MUST NOT 203 | change the meaning of the variables specified in this document. 204 | 205 | For example: 206 | 207 | Input: alice$wallet.com 208 | amount=10 209 | receiverPayId=pay$merchant.com 210 | assetCode=XRP 211 | assetScale=6 212 | network=XRPL 213 | nextUrl=https://merchant.com/thankyou 214 | PayID Easy Checkout URL: https://wallet.com/checkout 215 | Output: https://wallet.com/checkout?amount=100000&receiverPayId=payid%2Apay%24merchant.com&assetCode=XRP&assetScale=6&paymentNetwork=XRPL&nextUrl=https%3A%2F%2Fmerchant.com%2Fthankyou 216 | 217 | # PayID Easy Checkout JRDs 218 | This section defines the PayID Easy Checkout Link Relation, which conforms to section 4.4 of 219 | [Webfinger][RFC7033]. 220 | 221 | The Link MUST include the Link Relation Type defined in [PayID Easy Checkout URL](#iana-considerations) in the object's 'rel' field. 222 | The Link MUST also include a PayID Easy Checkout URL in the 'href' field of the link. 223 | 224 | * 'rel': `https://payid.org/ns/payid-easy-checkout-uri/1.0` 225 | * 'href': {A PayID Easy Checkout URL} 226 | 227 | The following is an example of a PayID Easy Checkout Link: 228 | 229 | { 230 | "rel": "https://payid.org/ns/payid-easy-checkout-uri/1.0", 231 | "href": "https://wallet.com/checkout" 232 | } 233 | 234 | # Security Considerations 235 | Various security considerations should be taken into account for PayID 236 | Easy Checkout. 237 | 238 | The security considerations for PayID Easy Checkout Discovery are discussed in 239 | section 6 of [PAYID-DISCOVERY][]. 240 | 241 | ## PayID Easy Checkout Redirection URI Manipulation 242 | When a sender uses the resource located at the PayID Easy Checkout URL, an attacker could manipulate 243 | the data encoded in the URL to trick the sender into sending a payment to a different PayID than was originally 244 | requested, or manipulate other parts of PayID Easy Checkout data to trick the sender. 245 | 246 | Additionally, if an attacker gains access to the merchant application, an attacker could replace the PayID Easy Checkout URL 247 | to execute a phishing or other attack. 248 | 249 | 250 | ## Access Control 251 | As with all web resources, access to the PayID Discovery resource could 252 | require authentication. See section 6 of [RFC7033][] for Access Control 253 | considerations. 254 | 255 | Furthermore, it is RECOMMENDED that PayID Discovery Servers only expose PayID Easy Checkout URLs 256 | which resolve to a protected resource (e.g., by logging into a wallet) before allowing access. 257 | 258 | # IANA Considerations 259 | ## New Link Relation Types 260 | This document defines the following Link relation type per [RFC7033][]. 261 | See section 3 for examples of each type of Link. 262 | 263 | ### PayID Easy Checkout URL 264 | 265 | * Relation Type ('rel'): `https://payid.org/ns/payid-easy-checkout-uri/1.0` 266 | * Media Type: `application/jrd+json` 267 | * Description: PayID Easy Checkout URL, version 1.0 268 | 269 | # Acknowledgments 270 | 271 | # Appendix 272 | 273 | ## Motivation 274 | The PayID Easy Checkout Protocol aims to enable a consistent user experience for senders paying for goods 275 | or services by standardizing the interaction between merchants/non-profits and customer/donor wallets. 276 | Given the ability to assign arbitrary metadata to a PayID as defined in [PayID-Discovery][], there is an opportunity 277 | to standardize the set of interactions between merchant and sender, specifically the process by which a merchant 278 | directs a sender to their digital wallet to complete a payment. 279 | The intention of this protocol is to enable an improved paying experience by reducing the number 280 | of steps a sender must take to complete a transaction. 281 | 282 | PayID Easy Checkout also limits the engineering effort needed to implement the protocol. 283 | Clients wishing to adopt this pattern should only need to implement UI-level changes in order to make the flow function 284 | as intended, which may aid in expanding overall adoption, further enhancing the protocol's user experience benefits. 285 | 286 | ### Design Goals 287 | 288 | #### Minimal effort for the Sender 289 | 290 | In order for a sender to checkout using the PayID Easy Checkout protocol, the sender only needs to provide a merchant 291 | with their PayID Easy Checkout enabled PayID. 292 | 293 | #### No New Server-Side Software 294 | 295 | Apart from a PayID Discovery compliant PayID Discovery Server, The PayID Easy Checkout Protocol does not require server-side 296 | software to be run by either the sender or merchant for a payment. The PayID Discovery Server is capable of providing details 297 | of where to send the sender via the PayID Discovery Protocol. Assuming the wallet used by the sender has implemented 298 | support in their UI for the PayID Easy Checkout Protocol, the sender can be redirected to their wallet 299 | to complete their transaction. 300 | 301 | ## Example Usage 302 | This section shows a non-normative example of PayID Easy Checkout between a hypothetical merchant (recipient) and sender. The merchant 303 | accepts payments using the PayID pay$merchant.example.com, and the sender controls the PayID alice$wallet.example.com. 304 | 305 | ### PayID Easy Checkout Initiation 306 | In this example, the sender might place some items in an online shopping cart on the merchant's web-site, then choose 307 | to checkout. The merchant would then render a form asking for the sender's PayID, as well as a "Checkout with PayID" 308 | button. Once the sender inputs their PayID `alice$wallet.example.com` and clicks the "Checkout with PayID" button, the merchant 309 | begins the PayID Easy Checkout flow. 310 | 311 | ### PayID Easy Checkout Wallet Discovery 312 | The merchant UI would first assemble the PayID Easy Checkout URL as defined in [PayID Easy Checkout Discovery](#payid-easy-checkout-discovery), 313 | yielding the URL `https://wallet.example.com/.well-known/webfinger?resource=payid%3Aalice%24wallet.example.com`. 314 | The merchant UI would then [query the assembled URL](#step-2-query-payid-easy-checkout-discovery-url). 315 | 316 | The HTTP request in this example would look like this: 317 | 318 | GET /.well-known/webfinger?resource=payid%3Aalice%24wallet.example.com 319 | Host: wallet.example.com 320 | 321 | If the sender's PayID Discovery Server has enabled PayID Easy Checkout in their wallet, the server would respond with something like this: 322 | 323 | HTTP/1.1 200 OK 324 | Access-Control-Allow-Origin: * 325 | Content-Type: application/jrd+json 326 | 327 | { 328 | "subject" : "payid:alice$wallet.example.com", 329 | "links" : 330 | [ 331 | { 332 | "rel": "https://payid.org/ns/payid-easy-checkout-uri/1.0", 333 | "template": "https://wallet.example.com/checkout" 334 | } 335 | ] 336 | } 337 | 338 | ### Assemble PayID Easy Checkout URL with Query Parameters 339 | The merchant UI would parse the PayID Discovery response and iterate over the 'links' collection to find the link with 340 | the Relation Type of "https://payid.org/ns/payid-easy-checkout-uri/1.0". The merchant UI would then add all of the query 341 | parameters defined in [PayID Easy Checkout URL Query Parameters](#payid-easy-checkout-url-query-parameters) to the URL included in the JRD Link. 342 | One query parameter of note is the "nextUrl" parameter, which allows the merchant to supply a redirect or callback URL 343 | for the sender's wallet to call once the sender has confirmed the payment. In this example, the merchant would like 344 | to display a "Thank You" page, and replaces `{nextUrl}` with `https://merchant.com/thankyou`. 345 | 346 | #### Correlating a Payment to an Invoice 347 | Merchants and non-profits will often need to correlate discrete layer-1 payments to an invoice or transaction entity 348 | in the merchants' native systems. The merchant in this example may have an invoice tracking system, on which an invoice 349 | gets created for the goods that the sender is buying, for example an invoice with a unique identifier of `1045464`. A common practice for correlating 350 | layer-1 payments to a specific transaction or invoice is to accept payments on a different layer-1 address for each invoice 351 | so that the merchant can listen for payments into that address and correlate the payment to the invoice. However, because 352 | the PayID Easy Checkout URL only provides the receiver's PayID, there is currently no way to associate the address that 353 | is given to the sender to the invoice. 354 | 355 | In order to accomplish this, a merchant could provide a unique PayID associated with an invoice, for example a PayID 356 | containing the invoice identifier, for each PayID Easy Checkout transaction. In this example, the merchant would first associate a payment address with the 357 | invoice ID, and would then redirect the sender to their wallet with the `receiverPayId` query parameter set to `pay-1045464$merchant.com`. 358 | When the merchant PayID Server receives a query for the address associated with that PayID, they could return the previously 359 | stored payment address. When the merchant receives a payment to that address, they can then associate the layer 1 payment 360 | with the invoice. 361 | 362 | ### Redirect Sender to Their Wallet 363 | Once the merchant UI populates the required query parameters in the URL template, the merchant UI redirects the sender to 364 | the Redirect URL so that the sender can confirm the payment. 365 | 366 | ### Sender Confirms Payment 367 | After the sender clicks the "Pay with PayID" button the merchant's UI, and the merchant performs the previous steps, 368 | the sender will be redirected to the Redirect URL, which is a front end resource of the wallet. The wallet UI can 369 | read the query parameters from the Redirect URL and render a confirmation page or modal with all of the required fields 370 | pre-populated. 371 | 372 | Once the sender confirms the payment, the wallet would perform a PayID address lookup on the "receiverPayId" query 373 | parameter to get the payment address of the merchant and submit a transaction to the underlying ledger or payment system. 374 | The merchant can then redirect the user back to the URL specified in the "nextUrl" query parameter, which will display 375 | the "Thank You" page of the merchant. 376 | -------------------------------------------------------------------------------- /src/spec/payid-uri.md: -------------------------------------------------------------------------------- 1 | --- 2 | coding: utf-8 3 | 4 | title: The 'payid' URI Scheme 5 | docname: draft-fuelling-payid-uri-01 6 | category: std 7 | 8 | pi: [toc, sortrefs, symrefs, comments] 9 | smart_quotes: off 10 | 11 | area: security 12 | author: 13 | 14 | - 15 | ins: D. Fuelling 16 | name: David Fuelling 17 | org: Ripple 18 | street: 315 Montgomery Street 19 | city: San Francisco 20 | region: CA 21 | code: 94104 22 | country: US 23 | 24 | normative: 25 | RFC2119: 26 | RFC3629: 27 | RFC3986: 28 | RFC5234: 29 | RFC5890: 30 | RFC5892: 31 | RFC8264: 32 | PAYID-DISCOVERY: 33 | title: "The PayID Discovery Protocol" 34 | target: https://tbd.example.com/ 35 | author: 36 | ins: D. Fuelling 37 | fullname: David Fuelling 38 | PAYID-PROTOCOL: 39 | title: "PayID Protocol" 40 | target: https://tbd.example.com/ 41 | author: 42 | - ins: A. Malhotra 43 | fullname: Aanchal Malhotra 44 | - ins: D. Schwartz 45 | fullname: David Schwartz 46 | VERIFIABLE-PAYID: 47 | title: "Verifiable PayID Protocol" 48 | target: https://tbd.example.com/ 49 | author: 50 | - ins: A. Malhotra 51 | fullname: Aanchal Malhotra 52 | - ins: D. Schwartz 53 | fullname: David Schwartz 54 | UNICODE: 55 | title: "The Unicode Standard" 56 | target: http://www.unicode.org/versions/latest/ 57 | author: 58 | surname: The Unicode Consortium 59 | fullname: The Unicode Consortium 60 | 61 | informative: 62 | RFC0020: 63 | RFC5988: 64 | RFC6068: 65 | RFC7033: 66 | RFC7565: 67 | RFC7595: 68 | 69 | --- note_Feedback 70 | This specification is a draft proposal, and is part of the 71 | [PayID Protocol](https://payid.org/) initiative. Feedback related to this 72 | document should be sent in the form of a Github issue at: 73 | https://github.com/payid-org/rfcs/issues. 74 | 75 | --- abstract 76 | This specification defines the 'payid' Uniform Resource Identifier (URI) 77 | scheme as a way to identify a payment account at a service provider. 78 | 79 | --- middle 80 | 81 | # Introduction 82 | Various Uniform Resource Identifier (URI) schemes can be used to 83 | identify a user account at a service provider. However, no standard 84 | identifier exists to identify a user's _payment_ account at a service 85 | provider. 86 | 87 | While popular URIs could be re-used as payment account identifiers, 88 | these identifiers are insufficient because they are typically recognized 89 | as supporting functionality unique to those schemes. For example, the 90 | 'mailto' scheme [RFC6068][] is broadly deployed for messaging. Re-using 91 | this identifier for payments would likely cause confusion because one 92 | desirable quality of a payment account identifier is that it expressly 93 | does not support messaging, in order to avoid spam and/or other security 94 | concerns such as phishing attacks. 95 | 96 | Deploying payment protocols on top of identifiers that are commonly 97 | employed for other use-cases would likely be a mis-use of those 98 | identifiers, and could also cause confusion for end-users, among other 99 | problems. 100 | 101 | Instead, the 'payid' scheme defines an identifier that is intended to 102 | identify accounts for payment use-cases only. 103 | 104 | # Terminology 105 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 106 | "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and 107 | "OPTIONAL" in this document are to be interpreted as described in 108 | [RFC2119][]. 109 | 110 | # Definition 111 | The syntax of the 'payid' URI scheme is defined in Section 7 of this 112 | document. 113 | 114 | A 'payid' URI identifies a payment account hosted at a service provider, 115 | and is designed for payment account identification rather than 116 | interaction, as discussed in section 1.2.2 of [RFC3986]. 117 | 118 | A 'payid' URI is constructed by taking a user's payment account identifier 119 | at a service provider and using that value as the 'acctpart'. The 'host' 120 | portion is then set to the DNS domain name of the service provider that 121 | provides the 'payid'. 122 | 123 | To compare two 'payid' URIs, case normalization and percent-encoding 124 | normalization (as specified in sections 6.2.2.1 and 6.2.2.2 of 125 | [RFC3986]) MUST be employed before performing any comparison. 126 | 127 | In addition, a 'payid' is case-insensitive and therefore should be 128 | normalized to lowercase. For example, the URI 129 | "PAYID:aLICE$www.EXAMPLE.com" is equivalent to 130 | "payid:alice$www.example.com". 131 | 132 | Note that both the 'acctpart' and 'host' components of a 'payid' may 133 | contain one or more dollar-sign characters. However, because a 'host' 134 | SHOULD also be a valid DNS domain, that portion of a 'payid' will 135 | generally not include a dollar-sign. Therefore, applications SHOULD 136 | always search for the last dollar-sign when attempting to parse a 'payid' 137 | URI into its two component parts. 138 | 139 | # Examples 140 | As an example, a user with an account name of "apollo" at a wallet 141 | service "wallet.example.com" can be identified by a URI using the 'payid' 142 | scheme via the following construction: 143 | 144 | 'payid:apollo$wallet.example.com' 145 | 146 | One possible PayID scenario is for an account to be registered with a 147 | payment service provider using an identifier that is associated with some 148 | other service provider. For example, a user with the email address 149 | "alice@example.net" might register with a wallet website whose domain 150 | name is "wallet.example.com". In order to facilitate payments to/from 151 | Alice, the wallet service provider might offer Alice a PayID using Alice's 152 | email address (though using an email address as a PayID is not 153 | recommended). In order to use Alice's email address as the 'acctpart' of 154 | the 'payid' URI, no percent-encoding is necessary because the 'acctpart' 155 | portion of a PayID allows for at-signs. Thus, the provisioned 'payid' URI 156 | for Alice would be "payid:alice@example.net$shoppingsite.example". 157 | 158 | Another possible scenario is where a payment service provider (e.g., a 159 | digital wallet) provides its users with PayIDs that are associated with 160 | the PayIDs of another service provider. For example, a user with the 161 | PayID "alice$bank.example.net" might register with a wallet website whose 162 | domain name is "wallet.example.net". In order to use the bank's PayID 163 | as the acctpart of the wallet's 'payid' URI, no percent-encoding is 164 | necessary because the 'acctpart' portion of a PayID allows for 165 | dollar-signs. Therefore, the resulting 'payid' URI would be 166 | "payid:alice$bank.example$wallet.example". 167 | 168 | The following example URIs illustrate several variations of PayIDs and 169 | their common syntax components: 170 | 171 | payid:alice$example.net 172 | 173 | payid:john.doe$example.net 174 | 175 | payid:jane-doe$example.net 176 | 177 | # Security Concerns 178 | The 'payid' URI scheme defined in this document does not directly enable 179 | interaction with a user's payment account and therefore does not present 180 | any direct security concerns. 181 | 182 | However, a 'payid' URI indicates existence of a payment account, so 183 | care should be taken to properly secure any payment account interactions 184 | allowed by a service provider. 185 | 186 | In addition, service providers and users should consider whether an 187 | attacker might be able to derive or infer other identifiers correlating 188 | to the user of any particular PayID. For example, replacing the `$` 189 | character in a PayID with an `@` sign SHOULD NOT yield a 'mailto' URI, 190 | when possible. In addition, care should be taken when the 'acctpart' of 191 | a PayID corresponds to a user's email address (in part or in whole) as this 192 | might allow an attacker to execute phishing attacks or send spam messages. 193 | 194 | Due to the use of percent-encoding in 'payid' URIs, implementers SHOULD 195 | disallow percent-encoded characters or sequences that would result in 196 | "space", "null", "control", or other characters that are otherwise 197 | forbidden. 198 | 199 | # Internationalization Concerns 200 | As specified in [RFC3986], the 'payid' URI scheme allows any character 201 | from the Unicode repertoire [Unicode] encoded as UTF-8 [RFC3629] and 202 | then percent-encoded into valid ASCII [RFC0020]. Before applying any 203 | percent-encoding, an application MUST ensure the following about the 204 | string that is used as input to the URI-construction process: 205 | 206 | * The 'acctpart' consists only of Unicode code points that conform to 207 | the PRECIS IdentifierClass specified in [RFC8264]. 208 | 209 | * The 'host' consists only of Unicode code points that conform to the 210 | rules specified in [RFC5892]. 211 | 212 | * Internationalized domain name (IDN) labels are encoded as A-labels 213 | [RFC5890]. 214 | 215 | # IANA Considerations 216 | In accordance with [RFC7595], this section provides the information 217 | needed to register the 'payid' URI scheme. 218 | 219 | **URI Scheme Name**: payid 220 | 221 | **Status**: permanent 222 | 223 | **URI Scheme Syntax**: The 'payid' URI syntax is defined here in Augmented 224 | Backus-Naur Form (ABNF) per [RFC5234], borrowing the 'host' and 'path' 225 | rules from [RFC3986]: 226 | 227 | payidURI = "payid" ":" acctpart "$" host 228 | acctpart = path 229 | 230 | Note that additional rules limit the characters that can be 231 | percent-encoded in a 'payid' URI. See "Encoding Considerations" below for 232 | more details. 233 | 234 | **URI Scheme Semantics**: The 'payid' URI scheme identifies payment 235 | accounts hosted at payment service providers. It is used only for 236 | identification, not interaction. 237 | 238 | **Encoding Considerations**: See Section 6 of this document. 239 | 240 | **Applications/Protocols That Use This URI Scheme Name**: The following 241 | protocols utilize this URI scheme: 242 | 243 | - [PAYID-DISCOVERY][], 244 | - [PAYID-PROTOCOL][], 245 | - [VERIFIABLE-PAYID][]. 246 | 247 | **Interoperability Considerations**: n/a. 248 | 249 | **Security Considerations**: See Section 6 of this document. 250 | 251 | **Contact**: rfcs@payid.org 252 | 253 | **Author/Change Controller**: TBD. 254 | 255 | **References**: None. 256 | 257 | # Acknowledgements 258 | This document was adapted from and heavily influenced by [RFC7565][], 259 | modifying it (in some cases only slightly) for a payments use-case. The 260 | author would like to acknowledge the contributions of everyone who worked 261 | on that and related specifications. 262 | 263 | In addition, the author would like to acknowledge everyone who provided 264 | feedback and use-cases for this derivative specification. 265 | -------------------------------------------------------------------------------- /src/spec/self-sov-verifiable-payid-protocol.md: -------------------------------------------------------------------------------- 1 | --- 2 | coding: utf-8 3 | 4 | title: Self-Sovereign Verifiable PayID 5 | docname: draft-aanchal-self-sov-verifiable-payid-protocol 6 | category: std 7 | 8 | pi: [toc, sortrefs, symrefs, comments] 9 | smart_quotes: off 10 | 11 | area: security 12 | author: 13 | 14 | - 15 | ins: A. Malhotra 16 | name: Aanchal Malhotra 17 | org: Ripple 18 | street: 315 Montgomery Street 19 | city: San Francisco 20 | region: CA 21 | code: 94104 22 | country: US 23 | phone: ----------------- 24 | email: amalhotra@ripple.com 25 | uri: https://www.ripple.com 26 | 27 | - 28 | ins: D. Schwartz 29 | name: David Schwartz 30 | org: Ripple 31 | street: 315 Montgomery Street 32 | city: San Francisco 33 | region: CA 34 | code: 94104 35 | country: US 36 | phone: ----------------- 37 | email: david@ripple.com 38 | uri: https://www.ripple.com 39 | 40 | normative: 41 | RFC2119: 42 | RFC2818: 43 | RFC4949: 44 | RFC5280: 45 | RFC6979: 46 | RFC7515: 47 | RFC7517: 48 | RFC7519: 49 | RFC7797: 50 | PAYID-URI: 51 | title: "The 'payid' URI Scheme" 52 | target: https://tbd.example.com/ 53 | author: 54 | ins: D. Fuelling 55 | fullname: David Fuelling 56 | PAYID-DISCOVERY: 57 | title: "PayID Discovery" 58 | author: 59 | ins: D. Fuelling 60 | fullname: David Fuelling 61 | PAYID-PROTOCOL: 62 | title: "PayID Protocol" 63 | author: 64 | ins: A. Malhotra 65 | fullname: Aanchal Malhotra 66 | ins: D. Schwartz 67 | fullname: David Schwartz 68 | 69 | informative: 70 | RFC4732: 71 | 72 | --- note_Feedback 73 | 74 | This specification is a draft proposal, and is part of the 75 | [PayID Protocol](https://payid.org/) initiative. Feedback related to this 76 | document should be sent in the form of a Github issue at: 77 | https://github.com/payid-org/rfcs/issues. 78 | 79 | --- abstract 80 | 81 | This specification defines one of the extensions of the Basic PayID protocol [PAYID-PROTOCOL][] that aims to enable trust-minimized PayID service. Specifically, this extension of Basic PayID protocol eliminates the trust requirement between the PayID owner and their PayID service provider by allowing PayID server operators (such as wallets/exchanges) to send payment account(s) address information associated with a PayID [PAYID-URI][] that is digitally signed with the PayID private key of the PayID owner along with PayID owner's `identity` information and other meta-data needed to verify the signature. As a result, Self-Sovereign Verifiable PayID enables PayID service providers to match the security model of applications such as non-custodial digital wallets. 82 | 83 | --- middle 84 | 85 | # Terminology 86 | This protocol can be referred to as `Self-Sovereign Verifiable PayID`. It uses the following terminology. 87 | 88 | * Endpoint: either the client or the server of a connection. 89 | * Sender: individual or entity originating a transaction. 90 | * PayID client: the endpoint that initiates PayID protocol/sending side of the transaction. 91 | * PayID server: the endpoint that returns payment account(s) address information in response to a PayID protocol request (non-custodial wallets, exchanges, etc). 92 | * PayID owner: individual or entity receiving a transaction. 93 | * Digital Signature: As defined in [RFC4949][]. 94 | 95 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119][] and [RFC9174][]. 96 | 97 | # Motivation 98 | 99 | While Self-Sovereign Verifiable PayID can be used in any context, its most immediate use case is to enable non-custodial service providers to provide hosted PayID service while preserving the existing trust assumptions between themselves and their users. 100 | 101 | Providers of PayID-enabled payment services can be broadly categorized as custodial and non-custodial, each of which operate under different security models. Non-custodial wallets/exchanges do not store their customers’ on-ledger private keys on their servers. Instead, these customers hold their private keys and hence are in full control of their funds. As such, there is no trust requirement between non-custodial wallets/exchanges and their customers, and these services are not responsible for any for lost, compromised or stolen private keys of their customers. Likewise, customers of non-custodial wallets/exchanges do not need to worry if the servers of those wallets/exchanges are compromised. 102 | 103 | Basic PayID protocol [PAYID-PROTOCOL][] specifies a protocol to interact with a PayID server and retrieve a payment account(s) address information resource along with other meta-data corresponding to the queried PayID. One of the security assumptions made by the basic PayID protocol that may be less desirable for some applications is that the owner of the PayID must trust their PayID server to provide correct and untampered responses. Under this model, the PayID server has full control over the contents of any PayID response message, with potentially adverse effects if the server goes rogue or is compromised. The PayID owner has no way of knowing if the PayID server behaves maliciously. This implicit trust assumption between the PayID owner and their PayID server is often unacceptable in a non-custodial setting. 104 | 105 | Self-Sovereign Verifiable PayID protocol allows a PayID owner to digitally sign a PayID response using a local application/device with their PayID private key (which never leaves their device). This signed PayID response can then be securely transferred to the non-custodial PayID service provider's server who can then send this as a response to a PayID query along with PayID owner's "identity" information. PayID clients can use this information to verify if a PayID response is signed by the PayID owner and then decide whether to proceed with any particular transaction. Consequently, the trust between a PayID owner and their PayID server to serve the correct mappings is removed. 106 | 107 | # Self-Sovereign Verifiable PayID Protocol Specification 108 | The Self-Sovereign Verifiable PayID protocol is designed along the same design principles as [PAYID-PROTOCOL][]. 109 | 110 | ## PaymentInformation Resource as JSON Web Signatures 111 | The PayID Protocol [PAYID-PROTOCOL] defines a Payment Account(s) Information Resource that contains information about a particular PayID. This document further refines this definition to allow this information to be digitally signed, and then represented as a JSON Web Signature (JWS) [RFC7515] using JWS JSON Serialization. 112 | 113 | Below, this document further defines the structure of each JWS component, for the purposes of Self-Sovereign Verifiable PayID protocol. 114 | 115 | ### JOSE Protected Header 116 | For JWS, the members of the JSON object represented by the JOSE Header describe the cryptographic operations applied to the JWS Protected header and the JWS payload and optionally additional properties of the JWS. 117 | 118 | For a complete list of members of this object, refer to [RFC7515][]. Following is a decoded JSON payload representing an example of JOSE protected header parameters as defined by the JWS JSON Serialization syntax. 119 | 120 | { 121 | "name": "identityKey", 122 | "alg" : "ES256K", 123 | "typ" : "JOSE+JSON", 124 | "b64" : false, 125 | "crit": ["b64"], 126 | "jwk" : { 127 | "kty": "EC", 128 | "use": "sig", 129 | "crv": "secp256k1", 130 | "x" : "0", 131 | "y" : "0", 132 | }, 133 | } 134 | 135 | #### name 136 | The `name` Header Parameter identifies the type of signature. It is a new OPTIONAL header parameter that is not defined in the IANA JSON Web Signature and Encryption Header Parameters Registry. 137 | 138 | #### alg 139 | The `alg` (algorithm) Header Parameter identifies the cryptographic algorithm used to secure the JWS. This is a required field as described in [RFC7515][]. We RECOMMEND using "ES256K" which is Elliptic Curve Digital Signature Algorithm (ECDSA) using secp256k1 curve-type and SHA-256 hash-type as defined in IANA JSON Web Signature and Encryption Header Parameters Registry. 140 | 141 | #### typ 142 | The `typ` (type) Header Parameter is used by JWS applications to declare the media type of the complete JWS as described in [RFC7515][]. If used, the value of `typ` field SHOULD be set to "JOSE+JSON". 143 | 144 | #### b64 145 | The `b64` (base64url-encode) Header Parameter is an extension to JWS specification that determines how a payload is represented in the JWS and the JWS signing input. When the "b64" value is `false`, the payload is represented simply as the JWS Payload value with no encoding; otherwise, it is represented as ASCII(BASE64URL(JWS Payload)). This is an optional field as described in [RFC7797][]. 146 | 147 | #### crit 148 | The `crit` (critical) Header Parameter indicates that extensions to JWS specification are being used that MUST be understood and processed. This is a required field to be used with "b64" parameter as described in [RFC7797][]. 149 | 150 | #### jwk 151 | The `jwk` (JSON Web Key) Header Parameter represents the public key that is used to digitally sign the JOSE header and JWS payload. This parameter is represented as a JSON Web Key as specified in [RFC7517][]. In the header above, members of "jwk" represent the properties of the public key, including its value that corresponds to the algorithm "ES256K". 152 | 153 | * `kty`: Identifies the cryptographic algorithm family used with the key, such as "EC" for Elliptic Curve. 154 | 155 | * `use`: Identifies the intended use of the public key, such as "sig" for signature. 156 | 157 | * `crv` : Indicates the elliptic curve and the hash type (e.g., "secp256k1" represents curve-type `secp256k1` and the hash-type `SHA-256`). 158 | 159 | * `x` : Indicates the X-coordinate of the corresponding public key. For "alg" parameter values of "ES256K" (which is from the ECDSA family), `x` contains the X-coordinate of the corresponding public key. 160 | 161 | * `y` : Indicates the Y-coordinate of the corresponding public key. For "alg" parameter values of "ES256K" (which is from the ECDSA family), `y` contains the Y-coordinate of the corresponding public key. 162 | 163 | Note: "jwk" is one way way of embedding public key in the JOSE header. For more details on other possible options for "alg" and representing public keys refer to [RFC7515][]. 164 | 165 | ### JWS Payload 166 | The JWS payload is the message that needs to be signed. 167 | 168 | { 169 | "exp" : 1596496501, 170 | "payId": "bob$wallet.com", 171 | "payIdAddress": { 172 | "expTime": 34874613475, 173 | "paymentNetwork": "XRPL", 174 | "environment": "TESTNET", 175 | "addressDetailsType": "CryptoAddressDetails", 176 | "addressDetails": { 177 | "address": "rnzBSt9ZCJSh4RxC9f1v6oS9WZtEYJa8B9", 178 | "tag": "12345" 179 | } 180 | } 181 | } 182 | 183 | #### exp 184 | The `exp` field is an optional field as described in [RFC7519][]. If used, it SHOULD be set to the expiration time of the cryptographic key used to generate the digital signature. 185 | 186 | #### payId 187 | The `payId` field is a required field. The value of `payId` field is the PayID URI in the client request that identifies the payment account information that the JSON object describes. 188 | 189 | #### PayIDAddress 190 | The `PayIDAddress` is a required field. The value of `PayIDAddress` field is a JSON object with the following keys: 191 | 192 | * "expTime": This is an optional field and follows the same structure as described for "exp" field in [RFC7519][]. If used, the value of `expTime` SHOULD be set to the maximum time upto which the payment address in the `address` field is valid. 193 | 194 | * "paymentNetwork": The value of the `paymentNetwork` is the value of payment-network string as specified in the client request's `Accept` header. 195 | 196 | * "environment": The value of `environment` string is the value of environment as specified in the client request's `Accept` header. 197 | 198 | * "addressDetailsType": The value of `addressDetailsType` is one of the following strings as described in [PAYID-PROTOCOL][]: 199 | 200 | * CryptoAddressDetails 201 | 202 | * FiatAddressDetails 203 | 204 | * "addressDetails": The value of `addressDetails` is the address information necessary to send payment on a specific `paymentNetwork` and `environment`. 205 | 206 | The `address` field MUST be present in the JWS payload. 207 | 208 | ### JWS signature 209 | The JWS signature is the digital signature which is calculated over the JOSE header and the JWS payload. 210 | 211 | "signature": "{base64Signature}" 212 | 213 | #### signature 214 | The value of `signature` is computed as described in [RFC7515][]. 215 | 216 | ## End-to-End Self-Sovereign Verifiable PayID protocol Flow 217 | A pre-requisite for this protocol requires the PayID owner to transfer signed `PaymentInformation` to the PayID server. This document specifies one such way of doing this. 218 | 219 | The following are the pre-steps that a PayID owner's device should perform locally: 220 | 221 | ### Generating PayID Key-pair 222 | We RECOMMEND using elliptic curve (EC) key type with Elliptic Curve Digital Signature Algorithm (ECDSA) with secp256k1 curve for creating JWS content. 223 | 224 | ### Generating JWS Token 225 | For each `payment-network` and `environment` that the PayID owner has a payment address for, generate the JOSE header, JWS Payload and JWS Signature as described above. A complete `PaymentInformation` response might look like: 226 | 227 | { 228 | 229 | "payId": "bob$wallet.com", 230 | "addresses": [], 231 | "verifiedAddresses": [ 232 | { 233 | "signatures": [ 234 | { 235 | "protected": { 236 | "name": "identityKey", 237 | "alg": "ES256K", 238 | "typ": "JOSE+JSON", 239 | "b64": "false", 240 | "crit": ["b64"], 241 | "jwk": { 242 | "kty": "EC", 243 | "use": "sig", 244 | "crv": "secp256k1", 245 | "x": "b8w36l6eCf7GyD5fvXp0Xj7ugdFuvYYcnmb1VRjBl5g=", 246 | "y": "Tp8RPAf4dWkd+K/BApSW/Ey5UJs53NOPJRqDNZzItPc=", 247 | }, 248 | }, 249 | "signature": "{base64Signature}", 250 | } 251 | ] 252 | "payload": { 253 | "exp" : 34874613475, 254 | "payId": "bob$wallet.com", 255 | "payIdAddress": { 256 | "expTime": 257 | "paymentNetwork": "XRPL", 258 | "environment": "TESTNET", 259 | "addressDetailsType": "CryptoAddressDetails", 260 | "addressDetails": { 261 | "address": "rnzBSt9ZCJSh4RxC9f1v6oS9WZtEYJa8B9", 262 | "tag": "12345" 263 | } 264 | } 265 | } 266 | } 267 | ] 268 | } 269 | 270 | * addresses: The `addresses` array is an OPTIONAL field. The implementations MAY choose to populate this field with payment address(es) information as per [PAYID-PROTOCOL][]. The implementations SHOULD refer to Security Considerations sections for the possible security trade-offs while using this field. 271 | 272 | * VerifiedAddresses: The `VerifiedAddresses` property is a required field. 273 | 274 | ### Posting signed response to non-custodial PayID service Provider's server 275 | Implementations SHOULD use a secure communication channel to transfer these resources to the PayID server. 276 | 277 | ## Basic Operations 278 | Following are the basic operations performed by a Self-Sovereign Verifiable PayID client and server to retrieve `PaymentInformation` resource corresponding to a PayID. 279 | 280 | ### PayID Client Requesting the PaymentInformation Resource 281 | When requesting the `PaymentInformation` resource, a Self-Sovereign Verifiable PayID client MAY use the same HTTP `GET` method as in [PAYID-PROTOCOL][] to the PayID URL without any query parameters and body. 282 | 283 | The PayID client MUST query the PayID server using HTTPS only. [RFC2818][] defines how HTTPS verifies the PayID server's identity. If the HTTPS connection cannot be established for any reason, then the PayID client MUST accept that the PayID request has failed and MUST NOT attempt to reissue the PayID request using HTTP over a non-secure connection. 284 | 285 | ### PayID Server Responding to the PaymentInformation Resource Request 286 | Upon receiving a `GET` request for a payment accounts(s) information resource or a `PaymentInformation` resource, a PayID server that supports Self-Sovereign Verifiable PayID protocol returns the `PaymentInformation` resource for the `payment-network` and `environment` requested by the PayID client in the request `Accept` header field, along with other required and/or optional metadata. 287 | 288 | However, if the PayID server does not support the Self-Sovereign Verifiable PayID protocol, the PayID server sends back a response as described in [PAYID-PROTOCOL][]. 289 | 290 | If the PayID server does not contain the payment accounts(s) information resource or a `PaymentInformation` resource resource corresponding to the request, the PayID server MUST respond with an appropriate error message. 291 | 292 | ### Parsing the PaymentInformation Response 293 | The PayID client MUST conform to the verification of JWS as specified in [RFC7515][]. 294 | 295 | # Example Use of Self-Sovereign Verifiable PayID Protocol 296 | This section shows sample use of this extension of Basic PayID protocol in a hypothetical scenario. 297 | 298 | ## Verifiable PayID Protocol by a Non-Custodial Wallet as PayID Server 299 | Suppose Alice wishes to send a friend some XRP from a web-based wallet provider that Alice has an account on. Alice would log-in to the wallet provider and enter Bob's PayID (say, `bob$wallet.com`) into the wallet UI to start the payment. 300 | The Wallet application would first discover the PayID URL for the PayID service-provider using one of the mechanisms described in PayID discovery [PAYID-DISCOVERY][] protocol. 301 | 302 | The Wallet application would then issue an HTTPS GET request: 303 | 304 | GET /users/bob HTTP/1.1 305 | Host: www.wallet.com 306 | Accept: application/xrpl-testnet+json 307 | PayID-version: 1.0 308 | 309 | Bob's wallet (e.g., a non-custodial wallet operating a PayID server) might respond like this: 310 | 311 | HTTP/1.1 200 OK 312 | Content-Type: application/json 313 | Content-Length: 403 314 | PayID-Version: 1.0 315 | Cache-Control: "no-store" 316 | Server: Apache/1.3.11 317 | { 318 | "payId": "bob$wallet.com", 319 | "addresses": [], 320 | "verifiedAddresses": [ 321 | { 322 | "signatures": [ 323 | { 324 | "protected": { 325 | "name": "identityKey", 326 | "alg": "ES256K", 327 | "typ": "JOSE+JSON", 328 | "b64": "false", 329 | "crit": ["b64"], 330 | "jwk": { 331 | "kty": "EC", 332 | "use": "sig", 333 | "crv": "secp256k1", 334 | "x": "b8w36l6eCf7GyD5fvXp0Xj7ugdFuvYYcnmb1VRjBl5g=", 335 | "y": "Tp8RPAf4dWkd+K/BApSW/Ey5UJs53NOPJRqDNZzItPc=", 336 | }, 337 | }, 338 | "signature": "base64Signature", 339 | } 340 | ] 341 | "payload": { 342 | "exp" : 1234574940, 343 | "payId": "bob$wallet.com", 344 | "payIdAddress": { 345 | "expTime": 34874613475, 346 | "paymentNetwork": "XRPL", 347 | "environment": "TESTNET", 348 | "addressDetailsType": "CryptoAddressDetails", 349 | "addressDetails": { 350 | "address": "T7CKYKhRujaxEs9fSxQwJApHsQVPKUgD7EtLWCGTAFBwTha" 351 | } 352 | } 353 | } 354 | } 355 | ] 356 | } 357 | 358 | In the above example, the `PaymentInformation` resource is a pre-signed message with the PayID private keys of the PayID owner Bob. Bob's non-custodial wallet retrieves this response and sends it to the PayID client. 359 | 360 | # Security Considerations 361 | This security considerations section only considers PayID clients and servers bound to implementations as defined in this document. 362 | 363 | The security guarantees mentioned in [PAYID-PROTOCOL][] apply to this protocol. In this section, we discuss the security model for Self-Sovereign Verifiable PayID protocol for non-custodial service providers. 364 | 365 | ## Security Model for Non-Custodial PayID Service Providers 366 | 367 | In the current security model, non-custodial wallets do not store their customers’ keys. Instead, wallet customers hold their private keys on their own device(s). There is a no trust requirement between the service provided by a non-custodial wallets and its customers. Because customers in this scenario hold the private keys: 368 | * Wallets are not liable for any consequences coming from the loss, compromise or theft of customers' private keys. 369 | * The non-custodial wallets do not require their customers to trust their servers in case wallets servers go malicious or are compromised. 370 | 371 | This extension of Basic PayID protocol preserves this trust model. Rather than requiring the PayID server to provide accurate PayID response for their customers, the PayID owners can generate these signed mappings with their own PayID private key locally on their app/device. The sender of the payment (PayID client wallet’s customer) can easily verify these signatures out-of-band with the receiver (i.e., PayID owner). This eliminates any risk of the non-custodial PayID server wallet losing its private keys, going malicious, getting hacked, or becoming otherwise compromised in a way that customers might lose funds. 372 | 373 | ## Using JSON Web Signatures 374 | The implementations of this extension of Basic PayID protocol MUST refer to the Security Considerations sections of [RFC7515][] and [RFC7519][]. 375 | 376 | ## Using addresses Array 377 | The `addresses` array in the PayID response is an array of unsigned payment addresses. Implementations of this extension of Basic PayID that choose to populate this array along with the `verifiedAddresses` array MAY be vulnerable to downgrade attacks. We RECOMMEND against populating this array unless absolutely necessary depending on the use-case. 378 | Also, note that this approach is not backwards-compatible with the PayID clients that do not understand Self-Sovereign Verifiable PayID protocol. 379 | 380 | # Privacy Considerations 381 | All privacy guarantees in the Privacy Considerations section of [PAYID-PROTOCOL][] apply to this extension of Basic PayID protocol. 382 | --------------------------------------------------------------------------------