docs: ToC, more detail & examples

This commit is contained in:
AJ ONeal 2021-10-17 00:51:12 -06:00
parent 7466e8a28e
commit 6e8aa2e09f
1 changed files with 239 additions and 23 deletions

248
README.md
View File

@ -17,13 +17,33 @@ The Good Documentation™ for the PayPal API (a.k.a. PayPal Checkout SDK) is the
subscriptions) subscriptions)
- <https://www.paypal.com/webapps/mpp/logos-buttons> (the buttons) - <https://www.paypal.com/webapps/mpp/logos-buttons> (the buttons)
# Table of Contents
- Install
- QuickStart
- Orders (One-Time Purchases)
- PayPal Checkout Buttons
- REST API Overview
- Orders & Subscriptions
- Redirects
- Webhooks
- Security & Notes
- Detailed Examples & Glossary
# Install # Install
```bash ```bash
npm install --save @root/paypal-checkout npm install --save @root/paypal-checkout
``` ```
# Usage # Quick Start
If you just want to create a "Buy Now" or "Checkout with PayPal" type of button,
here's the gist of what you need to do:
## Order (One-Time Purchase)
1. Initialize the API
```js ```js
"use strict"; "use strict";
@ -31,24 +51,169 @@ npm install --save @root/paypal-checkout
require("dotenv").config({ path: ".env" }); require("dotenv").config({ path: ".env" });
let PPC = require("@root/paypal-checkout"); let PPC = require("@root/paypal-checkout");
PPC.init({
client_id: "xxxx", PPC.init(
client_secret: "****", process.env.PAYPAL_CLIENT_ID || "xxxx",
process.env.PAYPAL_CLIENT_SECRET || "****",
"sandbox" || "live",
{
// default query params for endpoints that use them
prefer: "return=representation",
total_required: true,
page_size: 20,
}
);
```
2. Create a "Buy Now" link (for Approval)
```js
// See https://developer.paypal.com/docs/api/orders/v2/#orders_create
let order = await PPC.Order.createRequest({
application_context: {
brand_name: "Bliss via The Root Group, LLC",
shipping_preference: "NO_SHIPPING",
landing_page: "LOGIN",
user_action: "PAY_NOW",
return_url: `https://example.com/api/redirects/paypal-checkout/return`,
cancel_url: `https://example.com/api/redirects/paypal-checkout/cancel`,
},
purchase_units: [
{
request_id: "default",
custom_id: "my-local-db-id-for-user-purchasing-product",
// shown in PayPal Checkout Flow UI
description: "1 year of pure Bliss",
// on the charge (credit card) statement
soft_descriptor: "Bliss",
amount: {
currency_code: "USD",
value: "10.00",
},
},
],
}); });
PPC.Subscriptions.createRequest({ console.info(
// See https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions "Approve URL:",
order.links.find(function (link) {
return "approve" === link.rel;
}).href
);
```
3. Handle the redirect: Verify & Capture
```js
app.get("/api/redirects/paypal-checkout/return", async function (req, res) {
let orderId = req.query.token;
// verify that the user has paid
await PPC.Order.details(orderId)
.then(async function (order) {
console.info("Deliver the Product to:", order.payer.email_address);
if ("APPROVED" !== order.status) {
throw new Error("spoofed redirect or cancelled order");
}
// take the money
let capture = await PPC.Order.capture(order.id, {
final_capture: true,
});
})
.catch(next);
}); });
``` ```
4. Handle the PAYMENT.CAPTURE.COMPLETED WebHook
```js
app.get("/api/webhooks/paypal-checkout/:secret", async function (req, res) {
let crypto = require("crypto");
let secret = process.env.PAYPAL_WEBHOOK_SECRET || "";
let guess = req.params.secret;
if (
!secret ||
secret.length !== guess.length ||
!crypto.timingSafeEqual(Buffer.from(guess), Buffer.from(secret))
) {
next(new Error("bad webhook secret value"));
return;
}
let event = req.body;
switch (event.event_type) {
case "PAYMENT.CAPTURE.COMPLETED": {
let orderId = event.supplementary_data.related_ids.order_id;
let localDbId = event.custom_id;
console.info(
`Confirm that PayPal Order ${orderId} for ${localDbId} has been paid.`
);
}
default:
console.log("Ignoring", event.event_type);
res.json({ sucess: true });
return;
}
});
```
## PayPal Checkout Buttons
- <https://www.paypal.com/webapps/mpp/logos-buttons> <== THE ONE YOU WANT
- <img src="https://www.paypalobjects.com/webstatic/en_US/i/buttons/checkout-logo-large.png" alt="Check out with PayPal" />
- <https://developer.paypal.com/docs/checkout/>
- <https://www.paypal.com/buttons/>
# API # API
```txt ## Overview
PayPal.init(client_id, client_secret, 'sandbox|live', defaults);
```js
PayPal.init(client_id, client_secret, "sandbox", defaults);
PayPal.request({ method, url, headers, json }); PayPal.request({ method, url, headers, json });
``` ```
### Subscrptions (Recurring Payments) ## No Dependencies Needed
If you'd like to keep your code super lightweight, you don't even need an SDK -
you can just use simple HTTP requests:
```js
let qs = require("querystring");
let request = require("@root/request");
let paypalApi = "https://api-m.sandbox.paypal.com";
async function PayPalRequest(
endpoint = "/v2/checkout/orders",
query = { page_size: 20 },
body = { purchase_units: [] },
requestId // optional id for certain requests
) {
let search = qs.stringify(query);
return await request({
url: `${paypalApi}${endpoint}?${search}`,
auth: {
user: process.env.PAYPAL_CLIENT_ID,
pass: process.env.PAYPAL_CLIENT_SECRET,
},
headers: {
"PayPal-Request-Id": requestId,
},
json: body,
}).then(function (resp) {
if (rsep.status < 200 || resp.status >= 300) {
throw new Error("BAD RESPONSE");
}
return resp.toJSON().body;
});
}
```
### Subscriptions (Recurring Payments)
See https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions
```txt ```txt
// Webhook 'event_type': // Webhook 'event_type':
@ -85,7 +250,7 @@ See also:
- <https://developer.paypal.com/docs/api/orders/v2/#definition-purchase_unit_request> - <https://developer.paypal.com/docs/api/orders/v2/#definition-purchase_unit_request>
- <https://developer.paypal.com/docs/api/orders/v2/#definition-order_application_context> - <https://developer.paypal.com/docs/api/orders/v2/#definition-order_application_context>
# Redirects ### Redirects
- `return_url` - `return_url`
- `cancel_url` - `cancel_url`
@ -128,7 +293,7 @@ Also, PayPal presents the raw `cancel_url` and will NOT update the order or
subscription status. It's up to you to confirm with the user and change the subscription status. It's up to you to confirm with the user and change the
status to `CANCELLED`. status to `CANCELLED`.
# Webhooks ### Webhooks
Webhooks can be set up in the Application section of the Dashboard: Webhooks can be set up in the Application section of the Dashboard:
@ -185,12 +350,63 @@ Sandbox accounts (for creating fake purchases) can be managed at:
You can auth once and capture multiple times (unless you set `final_capture`). You can auth once and capture multiple times (unless you set `final_capture`).
## PayPal Checkout Buttons # Examples
- <https://www.paypal.com/webapps/mpp/logos-buttons> <== THE ONE YOU WANT ### Subscription.createRequest({ ... })
- <img src="https://www.paypalobjects.com/webstatic/en_US/i/buttons/checkout-logo-large.png" alt="Check out with PayPal" />
- <https://developer.paypal.com/docs/checkout/> See
- <https://www.paypal.com/buttons/> https://developer.paypal.com/docs/subscriptions/integrate/#use-the-subscriptions-api
```js
await Subscription.createRequest({
plan_id: plan.id,
//start_time: "2018-11-01T00:00:00Z", (must be in the future)
//quantity: "20",
//shipping_amount: { currency_code: "USD", value: "10.00" },
subscriber: {
name: { given_name: "James", surname: "Doe" },
email_address: "customer@example.com",
/*
shipping_address: {
name: { full_name: "James Doe" },
address: {
address_line_1: "123 Sesame Street",
address_line_2: "Building 17",
admin_area_2: "San Jose",
admin_area_1: "CA",
postal_code: "95131",
country_code: "US",
},
},
*/
},
application_context: {
brand_name: "Bliss via The Root Group, LLC",
locale: "en-US",
shipping_preference: Subscription.shipping_preferences.NO_SHIPPING,
user_action: Subscription.actions.SUBSCRIBE_NOW,
payment_method: {
payer_selected: Subscription.payer_selections.PAYPAL,
payee_preferred:
Subscription.payee_preferences.IMMEDIATE_PAYMENT_REQUIRED,
},
return_url:
"https://example.com/api/paypal-checkout/return?my_token=abc123",
cancel_url:
"https://example.com/api/paypal-checkout/cancel?my_token=abc123",
},
});
console.info("Subscription (Before Approval):");
console.info(JSON.stringify(subscription, null, 2));
console.info();
console.info(
"Approve URL:",
subscription.links.find(function (link) {
return "approve" === link.rel;
}).href
);
```
# Glossary # Glossary