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. \
|
but specificially designed to be (mostly) idiomatic JavaScript / Node.js. \
|
||||||
(excuse the `snake_case` - that's how the PayPal REST API is designed).
|
(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
|
```bash
|
||||||
npm install --save @root/paypal-checkout
|
npm install --save @root/paypal-checkout
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
```js
|
```js
|
||||||
"use strict";
|
"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
|
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
|
`constant`/`enum` representing an option you can pick from limited number of
|
||||||
options.
|
options.
|
||||||
|
@ -1,13 +1,34 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
let qs = require("querystring");
|
||||||
|
|
||||||
let request = require("@root/request");
|
let request = require("@root/request");
|
||||||
|
|
||||||
let PayPal = {};
|
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.__sandboxUrl = "https://api-m.sandbox.paypal.com";
|
||||||
PayPal.__baseUrl = PayPal.__sandboxUrl;
|
PayPal.__baseUrl = PayPal.__sandboxUrl;
|
||||||
PayPal.__id = client_id;
|
PayPal.__id = client_id;
|
||||||
PayPal.__secret = client_secret;
|
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) {
|
PayPal.request = async function _paypalRequest(reqObj) {
|
||||||
let headers = {};
|
let headers = {};
|
||||||
@ -24,6 +45,30 @@ PayPal.request = async function _paypalRequest(reqObj) {
|
|||||||
};
|
};
|
||||||
return await request(reqObj).then(sanitize);
|
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) {
|
function justBody(resp) {
|
||||||
return resp.body;
|
return resp.body;
|
||||||
@ -52,6 +97,14 @@ function must201or200(resp) {
|
|||||||
}
|
}
|
||||||
return 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) {
|
function enumify(obj) {
|
||||||
@ -127,15 +180,49 @@ Product.create = async function _createProduct({
|
|||||||
.then(must201or200)
|
.then(must201or200)
|
||||||
.then(justBody);
|
.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({
|
return await PayPal.request({
|
||||||
url: "/v1/catalogs/products?page_size=20&total_required=true",
|
url: `/v1/catalogs/products?${search}`,
|
||||||
json: true,
|
json: true,
|
||||||
})
|
})
|
||||||
.then(must201or200)
|
.then(must201or200)
|
||||||
.then(justBody);
|
.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 = {};
|
let Plan = {};
|
||||||
Plan.intervals = {
|
Plan.intervals = {
|
||||||
DAY: "DAY",
|
DAY: "DAY",
|
||||||
@ -209,16 +296,50 @@ Plan.create = async function _createPlan({
|
|||||||
.then(justBody);
|
.then(justBody);
|
||||||
};
|
};
|
||||||
|
|
||||||
Plan.list = async function _listPlans() {
|
Plan.list = async function _listPlans(query = {}) {
|
||||||
// TODO paging
|
query = Object.assign({}, PayPal.__defaultQuery, query);
|
||||||
|
let search = qs.stringify(query);
|
||||||
return await PayPal.request({
|
return await PayPal.request({
|
||||||
url: "/v1/billing/plans?page_size=20&total_required=true",
|
url: `/v1/billing/plans?${search}`,
|
||||||
json: true,
|
json: true,
|
||||||
})
|
})
|
||||||
.then(must201or200)
|
.then(must201or200)
|
||||||
.then(justBody);
|
.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 = {};
|
let Subscription = {};
|
||||||
Subscription.actions = {
|
Subscription.actions = {
|
||||||
CONTINUE: "CONTINUE",
|
CONTINUE: "CONTINUE",
|
||||||
@ -298,7 +419,7 @@ Subscription.createRequest = async function _createSubscription({
|
|||||||
.then(justBody);
|
.then(justBody);
|
||||||
};
|
};
|
||||||
|
|
||||||
Subscription.get = async function _getSubscription(id) {
|
Subscription.details = async function _getSubscription(id) {
|
||||||
return await PayPal.request({
|
return await PayPal.request({
|
||||||
url: `/v1/billing/subscriptions/${id}`,
|
url: `/v1/billing/subscriptions/${id}`,
|
||||||
json: true,
|
json: true,
|
||||||
@ -307,6 +428,23 @@ Subscription.get = async function _getSubscription(id) {
|
|||||||
.then(justBody);
|
.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.init = PayPal.init;
|
||||||
module.exports.request = PayPal.request;
|
module.exports.request = PayPal.request;
|
||||||
module.exports.Plan = Plan;
|
module.exports.Plan = Plan;
|
||||||
|
2
test.js
2
test.js
@ -141,7 +141,7 @@ async function test() {
|
|||||||
});
|
});
|
||||||
process.stdin.pause();
|
process.stdin.pause();
|
||||||
|
|
||||||
let s = await Subscription.get(subscription.id);
|
let s = await Subscription.details(subscription.id);
|
||||||
console.info("Subscription: (After Approval)");
|
console.info("Subscription: (After Approval)");
|
||||||
console.info(JSON.stringify(s, null, 2));
|
console.info(JSON.stringify(s, null, 2));
|
||||||
console.info();
|
console.info();
|
||||||
|
@ -16,13 +16,33 @@ let { Plan, Product, Subscription } = PayPal;
|
|||||||
async function test() {
|
async function test() {
|
||||||
let products = await Product.list();
|
let products = await Product.list();
|
||||||
console.info();
|
console.info();
|
||||||
console.info("Products:");
|
console.info("Products:", products.products.length);
|
||||||
console.info(JSON.stringify(products, null, 2));
|
//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();
|
let plans = await Plan.list();
|
||||||
console.info();
|
console.info();
|
||||||
console.info("Plans:");
|
console.info("Plans:", plans.plans.length);
|
||||||
console.info(JSON.stringify(plans, null, 2));
|
//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();
|
console.info();
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user