From 3e64b8b1d112063cc2a6e11e37d3c7e832952df0 Mon Sep 17 00:00:00 2001 From: aurelienshz Date: Fri, 25 Jun 2021 23:42:32 +0200 Subject: [PATCH] Initial commit --- README.md | 72 +++++++++++++++++++ SDK_reference.md | 162 +++++++++++++++++++++++++++++++++++++++++++ img/payment_flow.jpg | Bin 0 -> 31801 bytes payments.md | 66 ++++++++++++++++++ platform_API.md | 122 ++++++++++++++++++++++++++++++++ 5 files changed, 422 insertions(+) create mode 100644 README.md create mode 100644 SDK_reference.md create mode 100644 img/payment_flow.jpg create mode 100644 payments.md create mode 100644 platform_API.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..66ab09f --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +# Javascript SDK + +The JS SDK is the frontend SDK, designed to be used in your HTML pages or Single-Page Apps, served in the Pi Browser. + +In order to enable the SDK to function correctly, you need to declare your apps on the Developer Portal (open +develop.pi in the Pi Browser to access the Developer Portal). + +This SDK is **not** for a server-side NodeJS app. + + +## Installation + +Add the following `script` tag to all pages where you need the SDK to be available: + +```html + +``` + +This will load the Pi Network JS SDK as a global `window.Pi` object. + +## Usage + +### Authenticate a user + +You cannot perform any user-related operations (e.g read the user's info, request a payment from them) until you +have successfully authenticated the user. The first time, they will be presented with a dialog asking for +their consent to share their data with your app. + +```javascript +// Identify the user with their username / unique network-wide ID, and get permission to request payments from them. +const scopes = ['username', 'payments']; +function onOpenPaymentFound(payment) { /* ... */ }; // Read more about this in the SDK reference + +Pi.authenticate(scopes, onOpenPaymentFound).then(function(auth) { + console.log(`Hello ${auth.user.username}. Your unique ID is ${auth.user.pi_id}`); +}).catch(function(error) { + console.error(error); +}); +``` + +### Request a payment + +The `createPayment` method enables you to request a payment from the current user to your app's account. + +The user will be prompted with a modal provided by the Pi Wallet, enabling them to sign the +transaction and submit it to the Pi blockchain. + +```javascript + +const payment = Pi.createPayment({ + amount: 3.14, // Amount of π to be paid + reason: "Please pay for your order #1234", // User-facing explanation of the payment + metadata: { orderId: 1234, itemIds: [11, 42, 314] }, // Developer-facing metadata +}, { // Read more about those callbacks in the details docs linked below. + onPaymentIdReceived: onPaymentIdReceived, + onTransactionSubmitted: onTransactionSubmitted, + onPaymentCancelled: onPaymentCancelled, + onPaymentError: onPaymentError, +}); + +``` + +This code block is a **simplified example** to give you a sense of how it works. + +In order to make sure that all involved parties (your app, your server, the Pi servers, and the Pi blockchain) are in sync, +the payment needs to go through a **Server-Side Approval** flow and a **Server-Side Completion** flow. + +Please refer to: +* [the full Payments documentation](./payments.md) to learn about the complete payment flow +* [the Platform API documentation](./platform_API.md) to learn how to confirm the payment and acknowledge it from your + server +* the Demo App (coming soon!) to view an example of how you can implement the various required flows in your app's code. diff --git a/SDK_reference.md b/SDK_reference.md new file mode 100644 index 0000000..3868239 --- /dev/null +++ b/SDK_reference.md @@ -0,0 +1,162 @@ +# Client SDK reference: + +## Authentication + +```typescript +Pi.authenticate(scopes: Array, onIncompletePaymentFound: Function): Promise +``` + +Return value: + +```typescript +type AuthResult { + accessToken: string, + user: { + uid: string, + pi_id: string, + username: string + } +} +``` + +### `scopes` + +> **Not yet implemented** +> +> Currently, all calls to the `authenticate` method will assume all scopes have been requested. + +Here is a breakdown of various keys available on the `PiAuth['user']` object, and the scopes required for those keys +to be present: + +| Field | Description | Required Scope | +| -------------: | ------------- | :-------------: | +| `uid` | An app-local identifier for the user. This is specific to this user, and this app. It will change if the user revokes the permissions they granted to your app. | (none) | +| `username` | The user's Pi username. | `username` | +| `pi_id` | A network-wide identifier for this user. This will stay the same across all apps used by this user. | `username` | + + +### `onIncompletePaymentFound` + +Signature: `(payment: PaymentDTO) => void` + +Every time the user is authenticated, and when they try to start a new payment flow, the SDK checks that there is no incomplete payment for this user. An incomplete payment, in this context, is a payment which has been submitted to the Pi blockchain, but where `status.developer_completed` is still `false` (i.e. the developer has not called the `/complete` endpoint on this payment). + +If an incomplete payment is found, this callback will be invoked with the payment's `PaymentDTO`. + +When this callback is invoked, it is your responsibility to complete the corresponding payment (you should most likely send the payment DTO to your server, and process it according to your business logic). You'll need to do so before you can request a new payment from the user. + + +## Payments + +Create a new payment: + +```typescript +type PaymentData = { + amount: number, + reason: string, + metadata: Object, +}; + +type PaymentCallbacks = { + onPaymentIdReceived: (paymentId: string) => void, + onTransactionSubmitted: (paymentId: string, txid: string) => void, + onPaymentCancelled: (paymentId: string) => void, + onPaymentError: (error: Error, payment?: PaymentDTO) => void, +}; + +Pi.createPayment(paymentData: PaymentData, callbacks: PaymentCallbacks): void; +``` + +The `createPayment` method takes two argument: `paymentData` and `callbacks`. + +It will immediately start the payment flow, which will open on top of your app, enabling the user to review +the payment and submit the blockchain transaction, or reject it. + +> **Warning: concurrent payments:** +> +> When creating a new payment, if there is already an open payment with your app for the current user: +> * If the user has not yet made the blockchain transaction, the open payment will be cancelled. +> * If the user has already made the blockchain transaction, the new payment will be rejected +> (`onPaymentError` will be called) and the `onOpenPaymentFound` method will be called with the +> existing payment (use this callback to resolve the situation, e.g by completing the previous +> payment from your backend). + + + +## `paymentData` keys: + +### `amount` + +This is the amount that the user is requested to pay to your app. + +Example: `3.1415`. + +### `reason` + +The reason for this payment. This will be visible to the user. + +Example: `Please pay for order #1234`. + +### `metadata` + +An arbitrary object that you can attach to this payment. This is for your own use, and it won't +be shown to the user. You should use this object as a way to link this payment with your internal +business logic. + +Example: `{ orderId: 1234, itemIds: [11, 42, 314] }` + +## `callbacks` keys: + +### `onPaymentIdReceived` + +Signature: `(paymentId: string) => void` + +This is called when the payment identifier (paymentId) is obtained from Pi Servers. + +Use this callback to send the paymentId to your backend for **Server-Side Approval**. +Read more about Server-Side Approval and the full payment flow in the dedicated +[Payments](payments.md) page. + +### `onTransactionSubmitted` + +Signature: `(paymentId: string, txid: string) => void` + +This is called when the user has submitted the transaction to the Pi blockchain. + +Use this callback to send the blockchain transaction identifier (txid), along with the paymentId +to your backend for **Server-Side Completion**. +Read more about Server-Side Completion and the full payment flow in the dedicated +[Payments](payments.md) page. + +### `onPaymentCancelled` + +Signature: `(paymentId: string) => void` + +This is called when the payment is cancelled (by a user action, or programmatically). + +The payment may be cancelled either because the user manually rejected it, or because +of some other blocking situation: the user doesn't have enough funds on their account +to make the payment, another payment has been created concurrently... + +### `onPaymentError` + +Signature: `(error: Error, payment?: PaymentDTO) => void` + +This is called when an error occurs and the payment cannot be made. If the payment has been +created, the second argument will be present and you may use it to investigate the error. +Otherwise, only the first argument will be provided. + + +## Share dialog + +Open a native share dialog: + +```typescript +Pi.openShareDialog(title: string, message: string): void; +``` + +Use this method to open a native Share dialog (provided by the phone's OS), enabling your users to share +content from your app with their friends. + +* `title`: the title of the message being shared +* `message`: the message that will be sent when the user picks a target app in the Share flow diff --git a/img/payment_flow.jpg b/img/payment_flow.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2469058702a89f24a2fc181761ddb034cb379121 GIT binary patch literal 31801 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 JrUB;vHvw%~bN&DT literal 0 HcmV?d00001 diff --git a/payments.md b/payments.md new file mode 100644 index 0000000..e73508d --- /dev/null +++ b/payments.md @@ -0,0 +1,66 @@ +# Payments + +Payments are wrappers around blockchain transactions, which enable your app, +the Pi blockchain, and the Pi Servers to be all synchronized when the user +submits a blockchain transaction to pay for something in your app. + +They enable you, the developer of the app, to have full confidence that the +user has actually made the transaction, while not having to +bother with the technicalities involved when interacting with the Pi blockchain. + + +## The Payment flow + +After they're created, payments go through 3 major phases: + +1. Payment creation and Server-Side Approval +2. User interaction and blockchain transaction +3. Server-Side Completion + + +![Payment flow](./img/payment_flow.jpg) + +**Phase I - Payment creation and Server-Side Approval** + +1. `createPayment`: Your app's frontend creates the payment. The Payment Flow UI opens, but cannot be interacted with until the payment is approved by your server. + +2. `onPaymentIdReceived`: The JS SDK has obtained the payment identifier (PaymentID) and is passing it to your app for Server-Side approval. + +3. Your app's frontend sends the PaymentID to your app's server. This implementation is your responsibility. + +4. Server-Side Approval: Your app's server approves the payment with Pi Servers through the `/approve` API call. This enables the user to submit the blockchain transaction. + +**Phase II - User interaction and blockchain transaction** + +At this stage, the payment dialog becomes interactive and enables the +user to confirm the transaction, sign it, and submit it to the Pi blockchain. + +You do not have anything to do at this stage, everything is handled by the Pi +Apps Platform and the Pi Wallet. + +After the blockchain transaction is submitted, the payment flow will not close. +You need to acknowledge the payment through Server-Side completion before your +app is visible again. + + +**Phase III - Server-Side Completion** + +5. `onTransactionSubmitted`: The JS SDK passes the blockchain transaction identifier (TxID) to your app's frontend. You need this value for the Server-Side Completion flow. + +6. Your app's frontend sends the TxID to your app's server. This implementation is your responsibility. + +7. Server-Side Completion: Your app's server acknowledges the payment with Pi Servers through the `/complete` API call. This enables you to check whether the blockchain transaction has actually happened, and to let Pi know that +you're aware of it. + +8. The payment flow closes. Your app is now visible to the user again. +Your app's server and your app's frontend can exchange data, and +update the app interface to show a confirmation screen to the user. +This implementation is your responsibility. + + +> **The user might be lying to your app!** +> +> Users might be running a hacked version of the SDK, pretending that they +> have made a payment. If the API call for Server-Side completion +> returns a non-200 error code, **do not** mark the payment as complete on your +> side, and **do not** deliver whatever the user was trying to buy. diff --git a/platform_API.md b/platform_API.md new file mode 100644 index 0000000..5165f19 --- /dev/null +++ b/platform_API.md @@ -0,0 +1,122 @@ +# Platform API + +The platform API allows you to read and write data to the Pi Servers related with your app deployed on the +Pi App Platform, and your app's users. + +## Overview + +### Base path: + +The latest version of the Platform API is available at `api.minepi.com/v2`. + +> **Note about API versioning:** +> +> The platform API is currently in v2. +> As much as possible, we will not make any breaking changes to a version of an API, and only release breaking changes as +> new major versions. However, we might make breaking changes to an existing version without notice, if those are +> necessary (e.g security or privacy fixes). + +### Authorization + +The Platform API supports two different authorization mechanisms. + +#### Access token authorization + +Some API calls require that you provide a user's access token to access the resource. They are generally related with +a user's data (e.g: `/me`). Those endpoints can be accessed using the following Authorization header: + +``` +Authorization: Bearer +``` + +Those endpoints can be indifferently accessed from your backend / server app, or from your frontend / client app. + +#### Server API Key authorization + +For various reasons, some API calls must be made from your backend / server app. +Those endpoints can be accessed using the following Authorization header: + +``` +Authorization: Key +``` + +> **Warning: Server API keys are for servers only** +> +> Your Server API Key **must** be kept on your server, and must not be sent to clients (do not use it in your +> client javascript code). +> In the future, your server API key might enable sensitive operations on your app itself that your users should +> not be allowed to perform. Letting users access your server API key is a **serious security breach**. + + +## API Reference + +### Payments + +Base path: `/payments`. + +#### Create a payment: + +Do not create payments using the Platform API. Use the client-side Javascript SDK for this purpose. + +#### Get a payment: + +``` +GET /payments/:payment_id +``` + +* Authorization method: **User access token** +* Response type: [PaymentDTO](#PaymentDTO) + +> **Coming soon:** Server API Key Authorization on this endpoint, to remove the need from passing the user's access token +> to your server. + +#### Approve a payment: + +``` +POST /payments/:payment_id/approve +``` + +* Authorization method: **Server API Key** +* Response type: [PaymentDTO](#PaymentDTO) + +#### Complete a payment: + +``` +POST /payments/:payment_id/complete +``` + +* Authorization method: **Server API Key** +* Response type: [PaymentDTO](#PaymentDTO) + +## Resource types + +### `PaymentDTO` + +```typescript +{ + // Payment data: + "identifier": string, // The payment identifier + "user_uid": string, // The user's app-specific ID + "amount": number, // The payment amount + "reason": string, // A string provided by the developer, shown to the user + "metadata": Object, // An object provided by the developer for their own usage + "to_address": string, // The recipient address of the blockchain transaction + "created_at": string, // The payment's creation timestamp + + // Status flags representing the current state of this payment + "status": { + "developer_approved": boolean, // Server-Side Approval + "transaction_verified": boolean, // Blockchain transaction verified + "developer_completed": boolean, // Server-Side Completion + "cancelled": boolean, // Cancelled by the developer or by Pi Network + "user_cancelled": boolean, // Cancelled by the user + }, + + // Blockchain transaction data: + "transaction": null | { // This is null if no transaction has been made yet + "txid": string, // The id of the blockchain transaction + "verified": boolean, // True if the transaction matches the payment, false otherwise + "_link": string, // A link to the operation on the Blockchain API + }, +}; +```