mirror of
https://github.com/therootcompany/paypal-checkout.js.git
synced 2025-05-13 09:36:37 +00:00
feature!: more CRUD, also renamed .get to .details to match PayPal docs
This commit is contained in:
parent
82d225cafc
commit
0a9d277317
54
README.md
54
README.md
@ -5,10 +5,26 @@ with lots of abstraction without much value - this is very little abstraction,
|
||||
but specificially designed to be (mostly) idiomatic JavaScript / Node.js. \
|
||||
(excuse the `snake_case` - that's how the PayPal REST API is designed).
|
||||
|
||||

|
||||
|
||||
<img src="https://www.paypalobjects.com/webstatic/en_US/i/buttons/checkout-logo-large.png" alt="Check out with PayPal" />
|
||||
|
||||
The Good Documentation™ for the PayPal API (a.k.a. PayPal Checkout SDK) is the
|
||||
"REST API". See
|
||||
|
||||
- <https://developer.paypal.com/docs/api/orders/v2/> (one-time payments)
|
||||
- <https://developer.paypal.com/docs/api/subscriptions/v1/> (recurring
|
||||
subscriptions)
|
||||
- <https://www.paypal.com/webapps/mpp/logos-buttons> (the buttons)
|
||||
|
||||
# Install
|
||||
|
||||
```bash
|
||||
npm install --save @root/paypal-checkout
|
||||
```
|
||||
|
||||
# Usage
|
||||
|
||||
```js
|
||||
"use strict";
|
||||
|
||||
@ -25,15 +41,39 @@ PPC.Subscriptions.createRequest({
|
||||
});
|
||||
```
|
||||
|
||||
# API
|
||||
|
||||
```txt
|
||||
PayPal.init(client_id, client_secret, 'sandbox|live', defaults);
|
||||
PayPal.request({ method, url, headers, json });
|
||||
|
||||
PayPal.Product.create({ ... }); // event_type: "CATALOG.PRODUCT.CREATED"
|
||||
PayPal.Product.list();
|
||||
PayPal.Product.details(id);
|
||||
PayPal.Product.update(id, { description }); // event_type: "CATALOG.PRODUCT.UPDATED"
|
||||
|
||||
PayPal.Plan.create({ ... }); // event_type: "BILLING.PLAN.CREATED"
|
||||
PayPal.Plan.list();
|
||||
PayPal.Plan.details(id);
|
||||
PayPal.Plan.update(id, { description }); // event_type: "BILLING.PLAN.UPDATED"
|
||||
|
||||
PayPal.Subscription.create({ ... });
|
||||
PayPal.Subscription.details(id);
|
||||
PayPal.Subscription.cancel(id, { reason });
|
||||
```
|
||||
|
||||
# Webhooks
|
||||
|
||||
Webhooks can be set up in the Application section of the Dashboard:
|
||||
|
||||
- <https://developer.paypal.com/developer/applications>
|
||||
|
||||
You'll see a list of applications. Click on one to access the webhooks.
|
||||
|
||||
# Notes
|
||||
|
||||

|
||||
|
||||
The Good Documentation™ for the PayPal API (a.k.a. PayPal Checkout SDK) is the
|
||||
"REST API". See
|
||||
|
||||
- <https://developer.paypal.com/docs/api/orders/v2/> (one-time payments)
|
||||
- <https://developer.paypal.com/docs/api/subscriptions/v1/> (recurring
|
||||
subscriptions)
|
||||
|
||||
Note: Just about everything in the PayPal SDK that uses `ALL_CAPS` is a
|
||||
`constant`/`enum` representing an option you can pick from limited number of
|
||||
options.
|
||||
|
@ -1,13 +1,34 @@
|
||||
"use strict";
|
||||
|
||||
let qs = require("querystring");
|
||||
|
||||
let request = require("@root/request");
|
||||
|
||||
let PayPal = {};
|
||||
PayPal.init = function (client_id, client_secret) {
|
||||
PayPal.init = function (client_id, client_secret, env, opts) {
|
||||
if (!opts) {
|
||||
opts = {};
|
||||
}
|
||||
if (!("total_required" in opts)) {
|
||||
opts.total_required = true;
|
||||
}
|
||||
if (!opts.page_size) {
|
||||
opts.page_size = 20;
|
||||
}
|
||||
if (!opts.prefer) {
|
||||
opts.prefer = "return=representation";
|
||||
}
|
||||
|
||||
PayPal.__sandboxUrl = "https://api-m.sandbox.paypal.com";
|
||||
PayPal.__baseUrl = PayPal.__sandboxUrl;
|
||||
PayPal.__id = client_id;
|
||||
PayPal.__secret = client_secret;
|
||||
PayPal.__defaultQuery = {
|
||||
page_size: opts.page_size,
|
||||
total_required: opts.total_required,
|
||||
page: 1,
|
||||
};
|
||||
PayPal.__prefer = opts.prefer;
|
||||
};
|
||||
PayPal.request = async function _paypalRequest(reqObj) {
|
||||
let headers = {};
|
||||
@ -24,6 +45,30 @@ PayPal.request = async function _paypalRequest(reqObj) {
|
||||
};
|
||||
return await request(reqObj).then(sanitize);
|
||||
};
|
||||
PayPal._patch = function (obj) {
|
||||
let ops = [];
|
||||
|
||||
Object.keys(obj).forEach(function (k) {
|
||||
let val = obj[k];
|
||||
if ("undefined" === typeof val) {
|
||||
return;
|
||||
}
|
||||
|
||||
let op = "replace";
|
||||
if (null === val) {
|
||||
op = "delete";
|
||||
val = undefined;
|
||||
}
|
||||
|
||||
ops.push({
|
||||
path: `/${k}`,
|
||||
op: op,
|
||||
value: val,
|
||||
});
|
||||
});
|
||||
|
||||
return ops;
|
||||
};
|
||||
|
||||
function justBody(resp) {
|
||||
return resp.body;
|
||||
@ -52,6 +97,14 @@ function must201or200(resp) {
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
function must204or200(resp) {
|
||||
if (![200, 204].includes(resp.statusCode)) {
|
||||
let err = new Error("[@root/paypal-checkout] bad response");
|
||||
err.response = resp;
|
||||
throw err;
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
|
||||
/*
|
||||
function enumify(obj) {
|
||||
@ -127,15 +180,49 @@ Product.create = async function _createProduct({
|
||||
.then(must201or200)
|
||||
.then(justBody);
|
||||
};
|
||||
Product.list = async function _listProducts() {
|
||||
|
||||
Product.list = async function _listProducts(query = {}) {
|
||||
query = Object.assign({}, PayPal.__defaultQuery, query);
|
||||
let search = qs.stringify(query);
|
||||
return await PayPal.request({
|
||||
url: "/v1/catalogs/products?page_size=20&total_required=true",
|
||||
url: `/v1/catalogs/products?${search}`,
|
||||
json: true,
|
||||
})
|
||||
.then(must201or200)
|
||||
.then(justBody);
|
||||
};
|
||||
|
||||
Product.details = async function _showProductDetails(id) {
|
||||
return await PayPal.request({
|
||||
url: `/v1/catalogs/products/${id}`,
|
||||
json: true,
|
||||
})
|
||||
.then(must201or200)
|
||||
.then(justBody);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update product info
|
||||
* @param id
|
||||
* @param {{
|
||||
* description: string,
|
||||
* category: string,
|
||||
* image_url: string,
|
||||
* home_url: string,
|
||||
* }}
|
||||
*/
|
||||
Product.update = async function _updateProduct(
|
||||
id,
|
||||
{ description, category, image_url, home_url }
|
||||
) {
|
||||
let body = PayPal._patch({ description, category, image_url, home_url });
|
||||
return await PayPal.request({
|
||||
method: "PATCH",
|
||||
url: `/v1/catalogs/products/${id}`,
|
||||
json: body,
|
||||
}).then(must204or200);
|
||||
};
|
||||
|
||||
let Plan = {};
|
||||
Plan.intervals = {
|
||||
DAY: "DAY",
|
||||
@ -209,16 +296,50 @@ Plan.create = async function _createPlan({
|
||||
.then(justBody);
|
||||
};
|
||||
|
||||
Plan.list = async function _listPlans() {
|
||||
// TODO paging
|
||||
Plan.list = async function _listPlans(query = {}) {
|
||||
query = Object.assign({}, PayPal.__defaultQuery, query);
|
||||
let search = qs.stringify(query);
|
||||
return await PayPal.request({
|
||||
url: "/v1/billing/plans?page_size=20&total_required=true",
|
||||
url: `/v1/billing/plans?${search}`,
|
||||
json: true,
|
||||
})
|
||||
.then(must201or200)
|
||||
.then(justBody);
|
||||
};
|
||||
|
||||
Plan.details = async function _showPlanDetails(id) {
|
||||
return await PayPal.request({
|
||||
url: `/v1/billing/plans/${id}`,
|
||||
json: true,
|
||||
})
|
||||
.then(must201or200)
|
||||
.then(justBody);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update plan info
|
||||
* @param id
|
||||
* @param {{
|
||||
* description: string,
|
||||
* payment_preferences.auto_bill_outstandin: boolean,
|
||||
* taxes.percentage: string,
|
||||
* payment_preferences.payment_failure_threshold: number,
|
||||
* payment_preferences.setup_fee: string,
|
||||
* payment_preferences.setup_fee_failure_action: string,
|
||||
* }}
|
||||
*/
|
||||
Plan.update = async function _updatePlan(
|
||||
id,
|
||||
// TODO handle nested keys (ex: 'taxes.percentage')
|
||||
{ description /*, payment_preferences, taxes*/ }
|
||||
) {
|
||||
return await PayPal.request({
|
||||
method: "PATCH",
|
||||
url: `/v1/billing/plans/${id}`,
|
||||
json: PayPal._patch({ description }),
|
||||
}).then(must204or200);
|
||||
};
|
||||
|
||||
let Subscription = {};
|
||||
Subscription.actions = {
|
||||
CONTINUE: "CONTINUE",
|
||||
@ -298,7 +419,7 @@ Subscription.createRequest = async function _createSubscription({
|
||||
.then(justBody);
|
||||
};
|
||||
|
||||
Subscription.get = async function _getSubscription(id) {
|
||||
Subscription.details = async function _getSubscription(id) {
|
||||
return await PayPal.request({
|
||||
url: `/v1/billing/subscriptions/${id}`,
|
||||
json: true,
|
||||
@ -307,6 +428,23 @@ Subscription.get = async function _getSubscription(id) {
|
||||
.then(justBody);
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancel a subscription (prevent future auto billing)
|
||||
* @param id
|
||||
* @param {{
|
||||
* reason: string
|
||||
* }}
|
||||
*/
|
||||
Subscription.cancel = async function _showProductDetails(id, { reason }) {
|
||||
return await PayPal.request({
|
||||
method: "POST",
|
||||
url: `/v1/catalogs/products/${id}/cancel`,
|
||||
json: { reason },
|
||||
})
|
||||
.then(must201or200)
|
||||
.then(justBody);
|
||||
};
|
||||
|
||||
module.exports.init = PayPal.init;
|
||||
module.exports.request = PayPal.request;
|
||||
module.exports.Plan = Plan;
|
||||
|
2
test.js
2
test.js
@ -141,7 +141,7 @@ async function test() {
|
||||
});
|
||||
process.stdin.pause();
|
||||
|
||||
let s = await Subscription.get(subscription.id);
|
||||
let s = await Subscription.details(subscription.id);
|
||||
console.info("Subscription: (After Approval)");
|
||||
console.info(JSON.stringify(s, null, 2));
|
||||
console.info();
|
||||
|
@ -16,13 +16,33 @@ let { Plan, Product, Subscription } = PayPal;
|
||||
async function test() {
|
||||
let products = await Product.list();
|
||||
console.info();
|
||||
console.info("Products:");
|
||||
console.info(JSON.stringify(products, null, 2));
|
||||
console.info("Products:", products.products.length);
|
||||
//console.info(JSON.stringify(products, null, 2));
|
||||
|
||||
if (products.products.length) {
|
||||
let product = await Product.details(products.products[0].id);
|
||||
console.info("Product 0:");
|
||||
console.info(JSON.stringify(product, null, 2));
|
||||
|
||||
await Product.update(product.id, {
|
||||
description: `Product Description 10${Math.random()}`,
|
||||
});
|
||||
}
|
||||
|
||||
let plans = await Plan.list();
|
||||
console.info();
|
||||
console.info("Plans:");
|
||||
console.info(JSON.stringify(plans, null, 2));
|
||||
console.info("Plans:", plans.plans.length);
|
||||
//console.info(JSON.stringify(plans, null, 2));
|
||||
|
||||
if (plans.plans.length) {
|
||||
let plan = await Plan.details(plans.plans[0].id);
|
||||
console.info("Plan 0:");
|
||||
console.info(JSON.stringify(plan, null, 2));
|
||||
|
||||
await Plan.update(plan.id, {
|
||||
description: `Plan Description 20${Math.random()}`,
|
||||
});
|
||||
}
|
||||
|
||||
console.info();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user