diff --git a/README.md b/README.md
index 53cbf58..14ed0a1 100644
--- a/README.md
+++ b/README.md
@@ -41,12 +41,12 @@ Pi.authenticate(scopes, onIncompletePaymentFound).then(function(auth) {
});
```
-### Request a payment
+### Request a payment (User-To-App)
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.
+transaction and submit it to the Pi Blockchain.
```javascript
@@ -67,14 +67,21 @@ Pi.createPayment({
```
-This code block is a **simplified example** to give you a sense of how it works.
+### Request a payment (App-To-User)
-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.
+If you want to send Pi from your app to a user, you need to use one of Pi Network's backend SDKs, depending
+on the language your backend is written in. Refer to the [Advanced payments guide](./payments_advanced.md)
+for more information.
+
+
+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 (for User-to-App payment) and/or a **Server-Side Completion**
+flow (for all types of payments).
Please refer to:
* [the full Payments documentation](./payments.md) to learn about the complete payment flow
+* [the Advanced Payments documentation](./payments_advanced.md) to learn about App-to-User payment flow
* [the Platform API documentation](./platform_API.md) to learn how to confirm the payment and acknowledge it from your
server
* [the client SDK documentation](./SDK_reference.md) to learn about Pi Apps SDK and provided methods in detail
-* the Demo App (coming soon!) to view an example of how you can implement the various required flows in your app's code.
+* [the Demo App](https://github.com/pi-apps/demo) 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
index d9541eb..01042b0 100644
--- a/SDK_reference.md
+++ b/SDK_reference.md
@@ -1,4 +1,4 @@
-# Client SDK reference:
+# Client SDK reference
## Initialization
@@ -6,19 +6,24 @@ Add the following `script` tags to all pages where you need to call the Pi Apps
```html
-
+
```
The config object passed to the init function accepts the following keys:
-* `version` (string, required) - this is required to ensure compatibility of your app with newer SDK versions that might bring
+
+- `version` (string, required) - this is required to ensure compatibility of your app with newer SDK versions that might bring
breaking changes (in which case breaking changes will be implemented under a higher version number)
-* `sandbox`: (boolean, optional) - this enables you to configure the SDK to run in the sandbox.
+- `sandbox`: (boolean, optional) - this enables you to configure the SDK to run in the sandbox.
### Using the SDK in sandbox mode:
```html
-
+
```
You may now run your app in the sandbox environment (https://sandbox.minepi.com), provided you've configured
@@ -31,7 +36,6 @@ to configure this and view your Sandbox URL.
> `Pi.init({ version: "2.0", sandbox: <%= process.env.NODE_ENV !== 'production' %> })`. This depends on your
> setup, but running the Pi SDK in sandbox mode will generally happen whenever your app is running in development.
-
## Authentication
> **Warning:** The user information obtained with this method should not be passed to your backend and should
@@ -39,41 +43,35 @@ to configure this and view your Sandbox URL.
> **On your backend, use the Platform API as the source of truth.** You can verify the user's
> identity by requesting the /me endpoint from your backend, using the access token obtained with this method.
-
```typescript
-Pi.authenticate(scopes: Array, onIncompletePaymentFound: Function): Promise
+Pi.authenticate(scopes: Array, onIncompletePaymentFound: Function): Promise
```
Return value:
```typescript
type AuthResult = {
- accessToken: string,
+ accessToken: string;
user: {
- uid: string,
- username: string
- }
-}
+ uid: string;
+ username: string;
+ };
+};
```
### `scopes`
-Available scopes: `username`, `payments`.
-
-> **Not yet implemented**
->
-> Currently, all calls to the `authenticate` method will assume all scopes have been requested - i.e all calls
-> are interpreted as though the first argument were `['username', 'payments']`.
-> However, you should implement your app by only adding the scopes you need when calling `authenticate`.
-> Scopes support will be added before the general release of the Pi platform.
+Available scopes: `username`, `payments`, `wallet_address`
Here is a breakdown of various keys available on the `AuthResult['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` |
+| 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` |
+| `payments` | Required if your app needs to make payments between your app and the users | `payments` |
+| `wallet_address` | The wallet address of the user who authenticates on your app. | `wallet_address` |
### `onIncompletePaymentFound`
@@ -81,7 +79,7 @@ Signature: `(payment: PaymentDTO) => void`
Every time when 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
+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`.
@@ -90,7 +88,6 @@ When this callback is invoked, it is your responsibility to complete the corresp
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:
@@ -120,13 +117,12 @@ 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
-> (`onError` will be called) and the `onIncompletePaymentFound` callback that was passed to the `authenticate`
-> method will be called with the existing payment (use this callback to resolve the situation, e.g by sending
-> the previous payment to your server for server-side completion).
-
-
+>
+> - 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
+> (`onError` will be called) and the `onIncompletePaymentFound` callback that was passed to the `authenticate`
+> method will be called with the existing payment (use this callback to resolve the situation, e.g by sending
+> the previous payment to your server for server-side completion).
### `paymentData` keys:
@@ -168,7 +164,7 @@ Read more about Server-Side Approval and the full payment flow in the dedicated
Signature: `(paymentId: string, txid: string) => void`
-This is called when the user has submitted the transaction to the Pi blockchain. During the completion time period, this callback will be invoked multiple times in case of failure.
+This is called when the user has submitted the transaction to the Pi Blockchain. During the completion time period, this callback will be invoked multiple times in case of failure.
If the initial trial fails, the Pi SDK will continue to invoke the function roughly every 10 seconds until the completion timer ends.
Use this callback to send the blockchain transaction identifier (txid), along with the paymentId
@@ -194,40 +190,76 @@ This is called when an error occurs and the payment cannot be made. If the payme
created, the second argument will be present and you may use it to investigate the error.
Otherwise, only the first argument will be provided.
-
### Type `PaymentDTO`
-This type is used in the arguments that are passed to `onIncompletePaymentFound` and `onError`.
+This type is for the arguments that are passed to `onIncompletePaymentFound` and `onError`.
```typescript
type PaymentDTO = {
// Payment data:
- identifier: string, // The payment identifier
- user_uid: string, // The user's app-specific ID
- amount: number, // The payment amount
- memo: 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
-
+ identifier: string; // payment identifier
+ user_uid: string; // user's app-specific ID
+ amount: number; // payment amount
+ memo: string; // a string provided by the developer, shown to the user
+ metadata: Object; // an object provided by the developer for their own usage
+ from_address: string; // sender address of the blockchain transaction
+ to_address: string; // recipient address of the blockchain transaction
+ direction: Direction; // direction of the payment
+ created_at: string; // payment's creation timestamp
+ network: AppNetwork; // a network of the payment
+
// 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
- },
-
+ 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
- },
-}
+ transaction: null | {
+ // This is null if no transaction has been made yet
+ txid: string; // 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
+ };
+};
```
+### Type `Direction`
+
+A developer can check the direction of the payment with this type.
+
+```typescript
+type Direction = "user_to_app" | "app_to_user";
+```
+
+### Type `AppNetwork`
+
+Shows which network the payment is being made on.
+
+```typescript
+type AppNetwork = "Pi Network" | "Pi Testnet";
+```
+
+### Type `Scope`
+
+Scopes you can request to users.
+
+```typescript
+type Scope = "username" | "payments" | "wallet_address";
+```
+
+## Native features list
+
+Use this method to get a list of native features available for specific version of Pi Browser your user is using. Some SDK features may require particular features to work properly.
+
+```typescript
+type NativeFeature = "inline_media" | "request_permission" | "ad_network";
+
+Pi.nativeFeaturesList(): Promise>;
+```
## Share dialog
@@ -240,5 +272,80 @@ 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
+- `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
+
+## Ads
+
+SDK contains the `Ads` module (object) which allows developers to display ads to the users. It provides several methods for both interstitial and rewarded ads. All of the methods take advantage of Promise-based asynchronicity.
+
+```typescript
+type AdType = "interstitial" | "rewarded";
+```
+
+### Show ad
+
+Use `showAd(adType: "interstitial" | "rewarded")` method to display an ad to a user. It returns a promise which resolves with an object of type `ShowAdResponse`:
+
+```typescript
+type ShowAdResponse =
+ | {
+ type: "interstitial";
+ result: "AD_CLOSED" | "AD_DISPLAY_ERROR" | "AD_NETWORK_ERROR" | "AD_NOT_AVAILABLE";
+ }
+ | {
+ type: "rewarded";
+ result: "AD_REWARDED" | "AD_CLOSED" | "AD_DISPLAY_ERROR" | "AD_NETWORK_ERROR" | "AD_NOT_AVAILABLE" | "ADS_NOT_SUPPORTED" | "USER_UNAUTHENTICATED";
+ adId?: string;
+ };
+
+Pi.Ads.showAd(adType: AdType): Promise
+
+```
+
+- `"AD_NOT_AVAILABLE"` indicates the ad failed to load,
+- `"AD_CLOSED"` indicates the ad was successfully displayed and closed,
+- `"AD_REWARDED"` indicates the ad was successfully displayed and rewarded (only applicable for `rewarded` ads),
+- `"AD_DISPLAY_ERROR"` indicates the ad was successfully loaded but failed to be displayed,
+- `"AD_NETWORK_ERROR"` indicates that a user might have encountered network connection issues,
+- `"ADS_NOT_SUPPORTED"` - indicates that app version used by uesr does not support ads,
+- `"USER_UNAUTHENTICATED"` - indicates that user is not authenticated therefore rewarded ad cannot be display (only for `rewarded` ads).
+
+The Pi Browser internally manages the ads availability strategy, automatically loading initial ads and reloading them whenever displayed. This ensures that ads are consistently ready for display. However, in rare cases, loading ads may be interrupted or the third party app may use `showAd()` methods too quickly (even before the next ad is ready). To allow developers handle these cases, the Pi SDK provides additional methods: `isAdReady()` and `requestAd()`.
+
+
+
+> **Notes:**
+>
+> - If your application was approved to Pi Developer Ad Network, the response from `Pi.Ads.showAd('rewarded')` will contain additional `adId` fields, which allows you to [verify the rewarded status using Pi Platform API](ads.md#rewarded-ads-status-verification-with-pi-platform-api).
+> - If your application wasn't approved to Pi Developer Ad Network, the `adId` field will be missing from `Pi.Ads.showAd('rewarded')` response. You should not grant rewards to users without verifying the `adId` using [Pi Platform API](platform_API.md#verify-a-rewarded-ad-status).
+
+### Check if ad is ready
+
+Use `isAdReady(adType: "interstitial" | "rewarded")` method to check if an ad of specific type is available. This method returns a promise which resolves with object of type `IsAdReadyResponse`.
+
+```typescript
+type IsAdReadyResponse = {
+ type: "interstitial" | "rewarded";
+ ready: boolean;
+};
+
+Pi.Ads.isAdReady(adType: AdType): Promise
+```
+
+The Pi Browser internally manages the ads availability strategy, automatically loading initial ads and reloading them whenever displayed. This ensures that ads are consistently ready for display. However, in rare cases, loading ads may be interrupted or the third party app may use `showAd()` methods too quickly (even before the next ad is ready). To address such cases, the Pi SDK provides this method to allow developers to check the availability of ads explicitly.
+
+### Request ad
+
+Use `requestAd(adType: "interstitial" | "rewarded")` method to request an interstitial or a rewarded ad. It returns a promise which resolves with an object of type `RequestAdResponse`:
+
+```typescript
+type RequestAdResponse = {
+ type: "interstitial" | "rewarded";
+ result: "AD_LOADED" | "AD_FAILED_TO_LOAD" | "AD_NOT_AVAILABLE";
+};
+
+Pi.Ads.requestAd(adType: AdType): Promise
+```
+
+As with the Ads availability strategy, the Pi Browser internally manages the process of requesting new ads to replace the displayed ones. While it's not guaranteed that an ad will be available at all time, developers can use `requestAd()` method to manually retry the ad request in case a promise returned by the `isAdReady()` resolved with `false`.
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..034e848
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,21 @@
+# Security Policy
+
+## Supported Versions
+
+Use this section to tell people about which versions of your project are
+currently being supported with security updates.
+
+| Version | Supported |
+| ------- | ------------------ |
+| 5.1.x | :white_check_mark: |
+| 5.0.x | :x: |
+| 4.0.x | :white_check_mark: |
+| < 4.0 | :x: |
+
+## Reporting a Vulnerability
+
+Use this section to tell people how to report a vulnerability.
+
+Tell them where to go, how often they can expect to get an update on a
+reported vulnerability, what to expect if the vulnerability is accepted or
+declined, etc.
diff --git a/ads.md b/ads.md
new file mode 100644
index 0000000..8cf2c6c
--- /dev/null
+++ b/ads.md
@@ -0,0 +1,209 @@
+# Ads
+
+this document provides an introduction to ads and sample ads flows. For pure function definitions provided by Pi SDK, refer to [Ads section of SDK reference doc](SDK_reference.md#ads).
+
+## Introduction
+
+Pi App Platform gives developers the opportunity to integrate ads into their Pi App and get rewarded. Displaying ads is open to all applications in the Pi ecosystem, but only applications approved by Pi Core Team can be monetized.
+There are two type of ads currently supported: **interstitial** and **rewarded**. This documentation will be updated as new features are released.
+
+Technically, interstitial ads can be displayed at any time, but check out our best practices documentation to learn more.
+
+### Interstitial Ads
+
+Interstitial ads are full-screen ads and are meant to be displayed between some kind of content, usually at natural transition points or breaks, such as in between activities or game levels.
+
+### Rewarded Ads
+
+Rewarded ads are full-screen ads and are meant to be displayed to users in exchange for a reward within developer's app. For example if you're developing a game app and player runs out of lives but wants to continue playing, you might give him an option of obtaining extra live for watching a rewarded ad.
+
+Since rewarded ads are meant to reward a user, they can be displayed only if user of an app is authenticated.
+
+## Prerequisites
+
+In order to be rewarded from the Pi Developer Ad Network you must apply for it on the Pi Developer Portal. Once you’ve applied your application will be reviewed by the Pi Core Team.
+
+**Note**: some of the Ads module methods may return slightly different responses based on whether your application has been approved to Pi Developer Ad Network. These differences are always mentioned in the method definitions described in the [SDK reference](SDK_reference.md#ads).
+
+### Developer Ad Network Application
+
+Coming soon...
+
+### Support for Ad Network on user's Pi Browser
+
+Your users may be using old versions of Pi Browser, on which the Developer Ad Network is not available. You can ensure that Ad Network is available by using [`Pi.nativeFeaturesList` SDK method](SDK_reference.md#native-features-list) which returns an array of native features available on given Pi Browser build. In order for the ads to run properly, the native features list array should include `ad_network` string.
+
+```typescript
+// you usually would check the ads support ahead of time and store the information
+(async () => {
+ await Pi.init({ version: "2.0" });
+ const nativeFeaturesList = await Pi.nativeFeaturesList();
+ const adNetworkSupported = nativeFeaturesList.includes("ad_network");
+ // store adNetworkSupported for later use
+})();
+```
+
+If `ad_network` is missing, and you want to display ads to users, you can encourage them to update the Pi Browser.
+
+When promises returned from `Pi.Ads.requestAd` and `Pi.Ads.showAd` methods resolve with `"ADS_NOT_SUPPORTED"`, its synonymous to `"ad_network"` missing from the native features list array.
+
+For the in-depth definitions of Pi SDK methods used above, go to [SDK reference](SDK_reference.md#ads).
+
+## Implementation
+
+### Basic Usage
+
+The Pi Browser internally manages the ads availability strategy, automatically loading initial ads and reloading them whenever displayed. This aims to ensure that ads would be consistently ready for display. Therefore, in most cases you should be able to display ads to your users by simply calling the `Pi.Ads.showAd()` methods, passing ad type as an argument.
+
+#### Interstitial Ads
+
+Interstitial ads are simple to integrate. They are usually displayed around some transition points. For example, a level-based game app could display interstitial ads every 3 levels.
+
+```typescript
+// we pretend that the game app has just reached end of 3rd level according to above example
+completeLevel();
+
+if (currentLevel % 3 === 0) {
+ await Pi.Ads.showAd("interstitial");
+}
+
+startNewLevel();
+```
+
+#### Rewarded Ads
+
+Rewarded ads are meant to allow you to reward your users for watching an ad. Sticking to a game app example, if your game gives a user certain amount of lives and he runs out of them, you could offer him to watch an ad in exchange for one extra live.
+
+Rewarded ads are still simple to display, but require some more effort to ensure security and verify if the user should be rewarded. The `Pi.Ads.showAd("rewarded")` method returns a Promise with one additional field `adId`, with which you can verify validity of the Pi SDK response against Pi Platform API.
+
+> **The user might be cheating on your app!**
+>
+> Displaying a rewarded ad should lead to rewarding a user with some kind of a reward on your app.
+> Since users might be running a hacked version of the SDK and intercept your `displayAd('rewarded')` method, you must verify the rewarded status of the ad using Pi Platform API, before rewarding users.
+>
+> For documentation of Pi Platform API, refer to [Platform API documentation](platform_API.md#verify-a-rewarded-ad-status).
+>
+> **You should give rewards to your users only if `mediator_ack_status` for given ad is `"granted"`.**
+
+```typescript
+const showAdResponse = await Pi.Ads.showAd("rewarded");
+if (showAdResponse.result === "AD_REWARDED") {
+ // delegate rewarding user to your backend
+ // e.g.:
+ const result = await rewardUserForWatchingRewardedAd(showAdResponse.adId);
+ if (result.rewarded === true) {
+ grantUserAdditionalLive();
+ }
+}
+```
+
+### Advanced Usage
+
+While the examples from previous section are enough to have your application start displaying ads to your users, you might find yourself needing to perform additional actions like manually requesting ads or checking ads availability.
+
+In some cases, loading ads may fail or be interrupted. If you detect that an ad is not available, you can try manually calling `Pi.Ads.requestAd()` passing ad type as an argument.
+
+In other cases, you might need to know whether an ad is ready before calling `Pi.Ads.showAd()` method. You can check it using `Pi.Ads.isAdReady()` method passing ad type as an argument.
+
+For the full definitions of Pi SDK methods with all possible response types, check the [SDK reference](SDK_reference.md#ads).
+
+### Interstitial Ads Advanced Usage
+
+```typescript
+// we pretend that the game app has just reached end of 3rd level according to previous example
+
+completeLevel();
+
+if (currentLevel % 3 === 0) {
+ const isAdReadyResponse = await Pi.Ads.isAdReady("interstitial");
+
+ if (isAdReadyResponse.ready === true) {
+ return Pi.Ads.showAd("interstitial");
+ }
+
+ const requestAdResponse = await Pi.Ads.requestAd("interstitial");
+
+ if (requestAdResponse.result !== "AD_LOADED") {
+ // indicate to user that ad could not be loaded
+ return;
+ }
+
+ const showAdResponse = await Pi.Ads.showAd("interstitial");
+
+ if (showAdResponse.result !== "AD_CLOSED") {
+ // indicate to user that ad could not be displayed
+ return;
+ }
+}
+
+startNextLevel();
+```
+
+As you can see, combining the 3 methods you can react to non-happy paths at any step of the ads flow.
+
+### Rewarded Ads Advanced Usage
+
+Assuming that you've been approved to display ads on your app, here's a high level flow of displaying an ad to your user.
+
+1. User visits your app wants to watch an ad in order to be rewarded.
+2. Your app shows an ad on your app using the Pi SDK Ads module.
+
+- For documentation of Pi SDK methods, refer to [SDK reference](SDK_reference.md#ads).
+
+3. With `adId` obtained from the SDK method, you need to verify the status of the ad from the Pi Server. Details are available in the [next section](#rewarded-ads-status-verification-with-pi-platform-api).
+
+- For documentation of Pi Platform API, refer to [Platform API documentation](platform_API.md#verify-a-rewarded-ad-status).
+
+4. Once verified, you can reward the user.
+
+### Rewarded ads status verification with Pi Platform API
+
+> **The user might be cheating on your app!**
+>
+> Displaying a rewarded ad should lead to rewarding a user with some kind of a reward on your app.
+> Since users might be running a hacked version of the SDK and intercept your `displayAd('rewarded')` method, you must verify the rewarded status of the ad using Pi Platform API, before rewarding users.
+>
+> **You should give rewards to your users only if `mediator_ack_status` for given ad is `"granted"`.**
+
+```typescript
+const showRewardedAd = async () => {
+ try {
+ const isAdReadyResponse = await Pi.Ads.isAdReady("rewarded");
+
+ if (isAdReadyResponse.ready === false) {
+ const requestAdResponse = await Pi.Ads.requestAd("rewarded");
+
+ if (requestAdResponse.result === "ADS_NOT_SUPPORTED") {
+ // display modal to update Pi Browser
+ return showAdsNotSupportedModal();
+ }
+
+ if (requestAdResponse.result !== "AD_LOADED") {
+ // display modal ads are temporarily unavailable and user should try again later
+ return showAdUnavailableModal();
+ }
+ }
+
+ const showAdResponse = await Pi.Ads.showAd("rewarded");
+
+ if (showAdResponse.result === "AD_REWARDED") {
+ // reward user logic:
+ // usually delegate rewarding user to your backend which would
+ // firstly verify `adId` against Pi Platform API, then decide whether to
+ // reward the user and rewarded user if the rewarded ad status is confirmed
+ // e.g.:
+ const result = await rewardUserForWatchingRewardedAd(adId);
+ if (result.rewarded === true) {
+ showRewardedModal(result.reward);
+ } else {
+ showRewardFailModal(result.error);
+ }
+ } else {
+ // fallback logic
+ showAdErrorModal();
+ }
+ } catch (err) {
+ // good practice to handle any potential errors
+ }
+};
+```
diff --git a/authentication.md b/authentication.md
new file mode 100644
index 0000000..69d376c
--- /dev/null
+++ b/authentication.md
@@ -0,0 +1,18 @@
+# Authentication
+
+This is a simple walkthrough on handling user authentication using Pi SDK and API endpoint. For a detailed explanation, please refer to [Pi.authenticate](./SDK_reference.md#authentication) for SDK reference and [/me](./platform_API.md#authentication) for API reference.
+## Guide
+
+Here is a list of steps you can follow when you authenticate users for your app:
+### 1. Call `authenticate()` of Pi SDK
+
+Using the Pi SDK `authenticate()`, you can obtain user information along with the access token.
+```javascript
+const authRes = await window.Pi.authenticate(scopes, onIncompletePaymentFound);
+```
+
+### 2. Make a GET request to `/me` Pi API endpoint using the access token for verification
+To verify the data you got in step 1, you need to make a GET request to `/me` Pi API endpoint, with the access token included in the header. If the access token is valid, it will return a response with [UserDTO](./platform_API.md#UserDTO). However, if the token is invalid, it will return HTTP 401 Unauthorized code.
+```javascript
+const me = await axios.get('https://api.minepi.com/v2/me', {headers: {'Authorization': `Bearer ${accessToken}}});
+```
diff --git a/developer_portal.md b/developer_portal.md
index 6da6971..5427bfe 100644
--- a/developer_portal.md
+++ b/developer_portal.md
@@ -4,78 +4,88 @@ With the help of Pi SDK and Pi API, you can create applications that run within
## How to register your app
-1. The Pi Developer Portal can be accessed using the Pi Browser. Go to `pi://develop.pi` to visit the Pi Developer Portal. To register a new app, click on `New App` button at the bottom of the page.
+1. The Pi Developer Portal can be accessed using the Pi Browser. Go to `pi://develop.pi` to visit the Pi Developer Portal. When you first get to the main page, you won't see any app. To register your app, tab on the "New App" button.
-

+
+
-2. You can fill in the required fields. One important field is selecting the host network that the app is connecting to. Depending on this option, Pi SDK will automatically connect your app to the corresponding network. An app can only connect to one network at a time, and once you register the app, this option cannot be changed.
+2. Initially you're asked to fill in 3 required fields. The "App Network" option lets you select the host network that your app is connecting to. Depending on this option, Pi SDK will automatically connect your app to the corresponding network, either to Pi Mainnet or Pi Testnet. Note that an app can only connect to one network at a time, and once you register the app, this option cannot be changed. What this means is you're advised to create two different apps, one with testnet for testing purpose and one with mainnet for your production usage.
-

+
+
-> When the app is connecting to the Pi Testnet, you can maintain a list of whitelisted users. Your testnet app will only be accessible by users you allowed, and if you leave this field empty, anyone can access your test app.
+3. After you register your app, you can see your app in detail. Each app has its own "App Checklist", which helps you keep track of required steps to successfully set up your app. To see the checklist, tab on "App Checklist" button.
-

+
+
-> When the app is connecting to the Pi Mainnet, you can pair one of your testnet apps to the mainnet app. More specific usage is coming soon in the future.
+4. The checklist steps will be unlocked sequentially as you complete each step. The first step is to configure the hosting option.
-

+
+
-> When you're hosting the app on your server, you will also need to provide the url. Notice that you'll be asked to verify the ownership of the url after you register the app. After you filled in the required fields, you can submit to register your app by clicking on `Submit` button at the bottom of the page.
+5. Once you configure the hosting option, you'll see that "App Wallet" button is now enabled, and the next step for checklist is connecting a wallet to the app. Follow the instruction to create the app and connect it to your app.
-3. After you register your app, you can see your apps in the main page. If you selected the paired app for your mainnet app, it will show like the following.
+
+

+
+
+
+6. When you connect your app wallet to your app, the checklist will show you documentations that you can go over to get started. From here, you can have a look and follow the remaining steps to fully configure the app.
-

+
-
+
## Testnet app in the Pi Browser
If you registered your app to connect to the Pi Testnet and if you visit your app in the Pi Browser, it will show the black and yellow stripe at the top to indicate that this app is connected to the Pi Testnet.
-

+
-
-## Legacy Project created before 26/07/2022
+## Legacy Project created before 26/07/2022
Projects that existed prior to the launch of the Pi Mainnet SDK will point to the Pi Testnet
+
- This cannot be changed or updated
- It is best practice to create new Developer Portal Projects
### Developers and Teams with previously created Legacy Projects
-1. Create a new Developer Portal Project
- - Select Mainnet - This will be the Mainnet Version of the App
- - URL
- - This project should contain the desired URL that Pioneers will access the app with through the Pi Browser
- - URL ownership will be determined through verification process
- - Payment Wallet
- - Until developer/app wallets are created the wallet address of the developer who creates the new project page will be used to process all transactions
- - If the project page creator does not have a Pi Wallet Address which has been created in a Mainnet Migration all transactions will fail.
+1. Create a new Developer Portal Project
+ - Select Mainnet - This will be the Mainnet Version of the App
+ - URL
+ - This project should contain the desired URL that Pioneers will access the app with through the Pi Browser
+ - URL ownership will be determined through verification process
+ - Payment Wallet
+ - Until developer/app wallets are created the wallet address of the developer who creates the new project page will be used to process all transactions
+ - If the project page creator does not have a Pi Wallet Address which has been created in a Mainnet Migration all transactions will fail.
2. Create a second new Developer Project Page
- - Select Testnet - This will be for testing purposes
- - URL
- - Cannot be the same as the Mainnet URL
- - Must be owned by the developer as well
- - Set the desired access permissions
- - This project can be linked to the previously created Mainnet Project from step 1
+ - Select Testnet - This will be for testing purposes
+ - URL
+ - Cannot be the same as the Mainnet URL
+ - Must be owned by the developer as well
+ - Set the desired access permissions
+ - This project can be linked to the previously created Mainnet Project from step 1
3. Link Mainnet Project to the Brainstorm project (if applicable)
- - As of July 2022 it is possible to link only one developer portal project to a brainstorm project
+ - As of July 2022 it is possible to link only one developer portal project to a brainstorm project
### Special Considerations
+
- If an app team has no members with a Mainnet Wallet
- - Please reach out to the Pi CT through the Pi Support Portal
- - For the question “What does your question relate to? ”
- - Select “Pi Mainnet SDK Wallet”
- - Submit contact information
- - A member of the Pi CT will follow up
+ - Please reach out to the Pi CT through the Pi Support Portal
+ - For the question “What does your question relate to? ”
+ - Select “Pi Mainnet SDK Wallet”
+ - Submit contact information
+ - A member of the Pi CT will follow up
diff --git a/img/dev_portal_app_main_1.png b/img/dev_portal_app_main_1.png
new file mode 100644
index 0000000..ebe8587
Binary files /dev/null and b/img/dev_portal_app_main_1.png differ
diff --git a/img/dev_portal_app_main_2.png b/img/dev_portal_app_main_2.png
new file mode 100644
index 0000000..df929fe
Binary files /dev/null and b/img/dev_portal_app_main_2.png differ
diff --git a/img/dev_portal_checklist_1.png b/img/dev_portal_checklist_1.png
new file mode 100644
index 0000000..ffe680b
Binary files /dev/null and b/img/dev_portal_checklist_1.png differ
diff --git a/img/dev_portal_checklist_2.png b/img/dev_portal_checklist_2.png
new file mode 100644
index 0000000..58bd95f
Binary files /dev/null and b/img/dev_portal_checklist_2.png differ
diff --git a/img/dev_portal_create_app.png b/img/dev_portal_create_app.png
new file mode 100644
index 0000000..93060ea
Binary files /dev/null and b/img/dev_portal_create_app.png differ
diff --git a/img/dev_portal_main.png b/img/dev_portal_main.png
new file mode 100644
index 0000000..ae83b2c
Binary files /dev/null and b/img/dev_portal_main.png differ
diff --git a/img/dev_portal_main_1.png b/img/dev_portal_main_1.png
deleted file mode 100644
index b11c47e..0000000
Binary files a/img/dev_portal_main_1.png and /dev/null differ
diff --git a/img/dev_portal_main_2.png b/img/dev_portal_main_2.png
deleted file mode 100644
index 463decf..0000000
Binary files a/img/dev_portal_main_2.png and /dev/null differ
diff --git a/img/dev_portal_new_1.png b/img/dev_portal_new_1.png
deleted file mode 100644
index 87a0bb4..0000000
Binary files a/img/dev_portal_new_1.png and /dev/null differ
diff --git a/img/dev_portal_new_2.png b/img/dev_portal_new_2.png
deleted file mode 100644
index 3ba27e2..0000000
Binary files a/img/dev_portal_new_2.png and /dev/null differ
diff --git a/img/dev_portal_new_3.png b/img/dev_portal_new_3.png
deleted file mode 100644
index 9ae851f..0000000
Binary files a/img/dev_portal_new_3.png and /dev/null differ
diff --git a/payments.md b/payments.md
index 912a5fe..c8824ef 100644
--- a/payments.md
+++ b/payments.md
@@ -1,12 +1,12 @@
# 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
+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.
+bother with the technicalities involved when interacting with the Pi Blockchain.
## The Payment flow
@@ -33,7 +33,7 @@ After they're created, payments go through 3 major phases:
**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.
+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.
diff --git a/payments_advanced.md b/payments_advanced.md
new file mode 100644
index 0000000..6086988
--- /dev/null
+++ b/payments_advanced.md
@@ -0,0 +1,52 @@
+# Payments Advanced
+
+While the Pi Frontend Javascript SDK supports creating a U2A (User-To-App) payment, your app might need an ability to
+make an A2U (App-To-User) payment. In this documentation, we are going to show you how you can make an A2U payment.
+
+Please note that the A2U payments feature is currently available only on the Testnet.
+
+## Pi Backend SDKs
+
+Here's a list of Pi Backend SDKs for each language (or runtime) you can use for the A2U payment flow.
+| Language | Link | Status |
+| :---: | :---: | :---: |
+| Ruby | [pi-ruby](https://github.com/pi-apps/pi-ruby) | Officially supported by the Pi Core Team |
+| Node.js | [pi-nodejs](https://github.com/pi-apps/pi-nodejs) | **Coming soon!** - Officially supported by the Pi Core Team |
+| Python | TBD | Community-maintained |
+| PHP | TBD | Community-maintained |
+
+Community maintainers for other programming languages are more than welcome, please submit a PR in the [PiOS repository](https://github.com/pi-apps/PiOS) if youare interested to maintain a Pi SDK.
+
+
+## Supported features
+
+This section is a list of features supported by each backend SDK which enables integrating Pi into an app.
+Refer to the specific documentation of the library you're using in your project (see table in the previous section)
+for specifics on how to use each feature.
+
+### App-to-user payments
+
+The current design of A2U payments involves both interacting with the Pi Blockchain and the Pi backend. The Pi blockchain is obviously
+the only source of truth for exchanging Pi. The Pi backend is used to improve the end-user experience (e.g. provide users understandable memos
+on their wallet, and link backs to your app, while preserving user and developer privacy by not making this information public on the chain)
+and to assist developers in avoiding to make payment mistakes (e.g. double payments due to server faults)
+
+Benefits:
+
+- **Better user privacy:** This API enables safely accessing a user's wallet address as long as they have consented to
+sharing it with your app, and it encourages developers to only access the user's wallet when they have an actual
+intent to send them some π, enabling better privacy and user safety.
+
+- **User wallet accuracy:** The API returns the current user wallet if wallet change was necessary, to avoid the app sending Pi to a deprecated,
+inaccessible wallet address for the user.
+
+- **Better experience for users AND app developers:** Once a transaction has happened the blockchain, it can't be reversed.
+Letting the Pi backend servers know about your payment before making a transaction, making the transaction and then informing the backend server one
+more time enables recovering the associated metadata and blockchain transaction if a technical issue prevented it from being recorded in your database.
+This way the Pi backend, as a thrid party can help you avoid double payments or other bugs that might accidentally deplete your app wallets by accident.
+Of course, there are a lot of things that can go wrong and as the developers of your apps you are the only party responsible for managing your app's
+wallets and assets. The APIs here are provided for as-is and with no guarantees.
+
+- **Better integration with the Pi ecosystem:** Using the A2U API enables the payment to be shown in the user's wallet as
+coming from your app, instead of looking like a random transaction from an unknown address. This can allow beautiful usr experiences in the future such
+as direct links backs to your app from the users' wallets or thumbnail icons displaying the goods the users purchased or returned durectly in their wallets.
diff --git a/platform_API.md b/platform_API.md
index f4ca663..9a13f9d 100644
--- a/platform_API.md
+++ b/platform_API.md
@@ -47,22 +47,21 @@ Authorization: Key
> 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
### Authentication
#### Access a user's resource:
-Retrieve the user's information.
-
+Retrieve the user's information, including user information limited to what the user has consented to share
+with your app.
```
GET /me
```
-* Authorization method: **Access token**
-* Response type: [UserDTO](#UserDTO)
+- Authorization method: **Access token**
+- Response type: [UserDTO](#UserDTO)
Verify the data obtained with the frontend SDK (a malicious user could tamper with the requests and
send you wrong data) by sending the user’s access token to your backend and using this API endpoint
@@ -72,14 +71,38 @@ Access tokens are long, random strings, and the request will fail (401 HTTP erro
has been tampered with (a tampered token would
not belong to any real user).
-
### Payments
-Base path: `/payments`.
+There are two different payment types.
+
+1. U2A (User-To-App)
+2. A2U (App-To-User)
+
+#### Create a payment (U2A):
+
+If a payment type is U2A, use `createPayment` method of the client-side Javascript SDK to create a payment. You can refer to [Payments](./SDK_reference.md#payments) section for more details.
-#### Create a payment:
+#### Create a payment (A2U):
-Do not create payments using the Platform API. Use the client-side Javascript SDK for this purpose.
+```
+POST /payments
+```
+
+- Authorization method: **Server API Key**
+- Response type: [PaymentDTO](#PaymentDTO)
+
+Example request body:
+
+```
+{
+ "payment": {
+ "amount": 1,
+ "memo": "From app to user test",
+ "metadata": {"test": "test metadata"},
+ "uid": "a1111111-aaaa-bbbb-2222-ccccccc3333d"
+ }
+}
+```
#### Get a payment:
@@ -89,8 +112,8 @@ Get information about a payment.
GET /payments/{payment_id}
```
-* Authorization method: **Server API Key**
-* Response type: [PaymentDTO](#PaymentDTO)
+- Authorization method: **Server API Key**
+- Response type: [PaymentDTO](#PaymentDTO)
#### Approve a payment:
@@ -100,8 +123,8 @@ Server-side approval: mark a payment as approved, enabling the user to submit th
POST /payments/{payment_id}/approve
```
-* Authorization method: **Server API Key**
-* Response type: [PaymentDTO](#PaymentDTO)
+- Authorization method: **Server API Key**
+- Response type: [PaymentDTO](#PaymentDTO)
#### Complete a payment:
@@ -112,9 +135,8 @@ payment's txid, enabling the user to close the payment flow.
POST /payments/{payment_id}/complete
```
-
-* Authorization method: **Server API Key**
-* Response type: [PaymentDTO](#PaymentDTO)
+- Authorization method: **Server API Key**
+- Response type: [PaymentDTO](#PaymentDTO)
Example request body:
@@ -124,14 +146,59 @@ Example request body:
}
```
+#### Cancel a payment:
+
+Mark the payment as cancelled.
+
+```
+POST /payments/{payment_id}/cancel
+```
+
+- Authorization method: **Server API Key**
+- Response type: [PaymentDTO](#PaymentDTO)
+
+#### Get incomplete server payments:
+
+Returns the list of server payments (i.e A2U payments) which are in EITHER one of the two states below:
+
+- payment created, but no blockchain transaction was made yet ;
+- blockchain transaction submitted, but the payment has not been completed by the developer.
+
+```
+GET /payments/incomplete_server_payments
+```
+
+- Authorization method: **Server API Key**
+- Response type: { "incomplete_server_payments": Array<[PaymentDTO](#PaymentDTO)> }
+
+### Ads
+
+#### Verify a rewarded ad status
+
+Verify status of a rewarded ad by `adId` returned by client Pi SDK method `displayAd('rewarded')`
+
+```
+GET /ads_network/status/:adId
+```
+
+- Authorization method: **Server API Key**
+- Response type: [RewardedAdStatusDTO](#RewardedAdStatusDTO)
+
## Resource types
### `UserDTO`
```typescript
{
- "uid": string, // An app-specific user identifier
- "username": string, // The user's Pi username. Requires the `username` scope.
+ uid: string, // An app-specific user identifier
+ credentials: {
+ scopes: Array, // a list of granted scopes
+ valid_until: {
+ timestamp: number,
+ iso8601: string
+ }
+ },
+ username?: string, // The user's Pi username. Requires the `username` scope.
}
```
@@ -140,28 +207,42 @@ Example request body:
```typescript
{
// Payment data:
- "identifier": string, // The payment identifier
- "user_uid": string, // The user's app-specific ID
- "amount": number, // The payment amount
- "memo": 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
-
+ identifier: string, // payment identifier
+ user_uid: string, // user's app-specific ID
+ amount: number, // payment amount
+ memo: string, // a string provided by the developer, shown to the user
+ metadata: Object, // an object provided by the developer for their own usage
+ from_address: string, // sender address of the blockchain transaction
+ to_address: string, // recipient address of the blockchain transaction
+ direction: Direction, // direction of the payment
+ created_at: string, // the payment's creation timestamp
+ network: AppNetwork, // a network of the payment
+
// 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
+ 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
+ transaction: null | { // This is null if no transaction has been made yet
+ txid: string, // 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
},
};
```
+
+### `RewardedAdStatusDTO`
+
+```typescript
+{
+ "identifier": string; // the adId token returned from the Pi SDK displayAd("rewarded") method
+ "mediator_ack_status": "granted" | "revoked" | "failed" | null;
+ "mediator_granted_at": string | null; // ISO 8601 date string
+ "mediator_revoked_at": string | null; // ISO 8601 date string
+}
+```