mirror of
https://github.com/therootcompany/paypal-checkout.js.git
synced 2025-05-13 17:46:42 +00:00
initial commit
This commit is contained in:
commit
69ed827e3c
111
.gitignore
vendored
Normal file
111
.gitignore
vendored
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
privkey.*
|
||||||
|
.env.*
|
||||||
|
lib/categories.json
|
||||||
|
|
||||||
|
# vim swap files
|
||||||
|
.*.sw*
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# TypeScript v1 declaration files
|
||||||
|
typings/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
.env.test
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and *not* Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
7
.jshintrc
Normal file
7
.jshintrc
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"esversion": 11,
|
||||||
|
"node": true,
|
||||||
|
"browser": true,
|
||||||
|
"curly": true,
|
||||||
|
"sub": true
|
||||||
|
}
|
1
.prettierignore
Normal file
1
.prettierignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.min.js
|
8
.prettierrc.json
Normal file
8
.prettierrc.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 80,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"singleQuote": false,
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"semi": true,
|
||||||
|
"proseWrap": "always"
|
||||||
|
}
|
6
LICENSE
Normal file
6
LICENSE
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
Copyright AJ ONeal 2021
|
||||||
|
Copyright The Root Group, LLC 2021
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
65
README.md
Normal file
65
README.md
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# @root/paypal-checkout
|
||||||
|
|
||||||
|
In contrast to the official PayPal Checkout SDK - which is auto-generated code
|
||||||
|
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).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install --save @root/paypal-checkout
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
require("dotenv").config({ path: ".env" });
|
||||||
|
|
||||||
|
let PPC = require("@root/paypal-checkout");
|
||||||
|
PPC.init({
|
||||||
|
client_id: "xxxx",
|
||||||
|
client_secret: "****",
|
||||||
|
});
|
||||||
|
|
||||||
|
PPC.Subscriptions.create({
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Sandbox accounts (for creating fake purchases) can be managed at:
|
||||||
|
<https://developer.paypal.com/developer/accounts>
|
||||||
|
|
||||||
|
Note on Auth + Capture:
|
||||||
|
|
||||||
|
> Authorization and capture enables you to authorize fund availability but delay
|
||||||
|
> fund capture. This can be useful for merchants who have a delayed order
|
||||||
|
> fulfillment process. Authorize & Capture also enables merchants to change the
|
||||||
|
> original authorization amount in case the order changes due to shipping,
|
||||||
|
> taxes, or gratuity.
|
||||||
|
>
|
||||||
|
> For any payment type, you can capture less than or the full original
|
||||||
|
> authorized amount. You can also capture up to 115% of or $75 USD more than the
|
||||||
|
> original authorized amount, whichever is less.
|
||||||
|
>
|
||||||
|
> See
|
||||||
|
>
|
||||||
|
> - <https://developer.paypal.com/docs/admin/auth-capture/>
|
||||||
|
> - <https://developer.paypal.com/docs/api/payments/v2/#authorizations_capture>
|
||||||
|
|
||||||
|
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/>
|
9
example.env
Normal file
9
example.env
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# shellcheck disable=SC2034
|
||||||
|
|
||||||
|
# Set to 'production' for the Live PayPal API
|
||||||
|
NODE_ENV=development
|
||||||
|
|
||||||
|
PAYPAL_CLIENT_ID=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||||
|
PAYPAL_CLIENT_SECRET=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||||
|
PAYPAL_SANDBOX_EMAIL=sb-xxxxxxxxxxxx@personal.example.com
|
||||||
|
PAYPAL_SANDBOX_PASSWORD="********"
|
47
package-lock.json
generated
Normal file
47
package-lock.json
generated
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"name": "@root/paypal-checkout",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"lockfileVersion": 2,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "@root/paypal-checkout",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@root/request": "^1.7.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"dotenv": "^10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@root/request": {
|
||||||
|
"version": "1.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@root/request/-/request-1.7.0.tgz",
|
||||||
|
"integrity": "sha512-lre7XVeEwszgyrayWWb/kRn5fuJfa+n0Nh+rflM9E+EpC28yIYA+FPm/OL1uhzp3TxhQM0HFN4FE2RDIPGlnmg=="
|
||||||
|
},
|
||||||
|
"node_modules/dotenv": {
|
||||||
|
"version": "10.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
|
||||||
|
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@root/request": {
|
||||||
|
"version": "1.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@root/request/-/request-1.7.0.tgz",
|
||||||
|
"integrity": "sha512-lre7XVeEwszgyrayWWb/kRn5fuJfa+n0Nh+rflM9E+EpC28yIYA+FPm/OL1uhzp3TxhQM0HFN4FE2RDIPGlnmg=="
|
||||||
|
},
|
||||||
|
"dotenv": {
|
||||||
|
"version": "10.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
|
||||||
|
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
package.json
Normal file
28
package.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "@root/paypal-checkout",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "A more sensible, human-generated wrapper for the PayPal Checkout REST API",
|
||||||
|
"main": "paypal-checkout.js",
|
||||||
|
"files": ["lib"],
|
||||||
|
"scripts": {
|
||||||
|
"postinstall": "node utils/make-categories.js",
|
||||||
|
"test": "node test.js"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/therootcompany/paypal-checkout.js.git"
|
||||||
|
},
|
||||||
|
"keywords": ["paypal", "checkout", "sdk", "rest", "api", "subscriptions"],
|
||||||
|
"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/therootcompany/paypal-checkout.js/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/therootcompany/paypal-checkout.js#readme",
|
||||||
|
"devDependencies": {
|
||||||
|
"dotenv": "^10.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@root/request": "^1.7.0"
|
||||||
|
}
|
||||||
|
}
|
295
paypal-checkout.js
Normal file
295
paypal-checkout.js
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
let request = require("@root/request");
|
||||||
|
|
||||||
|
let PayPal = {};
|
||||||
|
PayPal.init = function (id, secret) {
|
||||||
|
PayPal.__sandboxUrl = "https://api-m.sandbox.paypal.com";
|
||||||
|
PayPal.__baseUrl = PayPal.__sandboxUrl;
|
||||||
|
PayPal.__id = id;
|
||||||
|
PayPal.__secret = secret;
|
||||||
|
};
|
||||||
|
PayPal.request = async function _paypalRequest(reqObj) {
|
||||||
|
let headers = {};
|
||||||
|
if (reqObj.id) {
|
||||||
|
// Optional and if passed, helps identify idempotent requests
|
||||||
|
headers["PayPal-Request-Id"] = reqObj.id;
|
||||||
|
}
|
||||||
|
// ex: https://api-m.sandbox.paypal.com/v1/billing/subscriptions
|
||||||
|
reqObj.url = `${PayPal.__baseUrl}${reqObj.url}`;
|
||||||
|
reqObj.headers = Object.assign(headers, reqObj.headers || {});
|
||||||
|
reqObj.auth = {
|
||||||
|
user: PayPal.__id,
|
||||||
|
pass: PayPal.__secret,
|
||||||
|
};
|
||||||
|
return await request(reqObj).then(sanitize);
|
||||||
|
};
|
||||||
|
|
||||||
|
function justBody(resp) {
|
||||||
|
return resp.body;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitize(resp) {
|
||||||
|
resp = resp.toJSON();
|
||||||
|
Object.keys(resp.headers).forEach(function (k) {
|
||||||
|
if (k.toLowerCase().match(/Auth|Cookie|Token|Key/i)) {
|
||||||
|
resp.headers[k] = "[redacted]";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.keys(resp.request.headers).forEach(function (k) {
|
||||||
|
if (k.toLowerCase().match(/Auth|Cookie|Token|Key/i)) {
|
||||||
|
resp.request.headers[k] = "[redacted]";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
function must201or200(resp) {
|
||||||
|
if (![200, 201].includes(resp.statusCode)) {
|
||||||
|
let err = new Error("bad response");
|
||||||
|
err.response = resp;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
function enumify(obj) {
|
||||||
|
Object.keys(obj).forEach(function (k) {
|
||||||
|
obj[k] = k;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
let Product = {};
|
||||||
|
|
||||||
|
// SaaS would be type=SERVICE, category=SOFTWARE
|
||||||
|
Product.types = {
|
||||||
|
DIGITAL: "DIGITAL",
|
||||||
|
PHYSICAL: "PHYSICAL",
|
||||||
|
SERVICE: "SERVICE",
|
||||||
|
};
|
||||||
|
Product.__typeNames = Object.keys(Product.types);
|
||||||
|
|
||||||
|
// Documented under "categories" at
|
||||||
|
// https://developer.paypal.com/docs/api/catalog-products/v1/
|
||||||
|
Product.categories = require("./lib/categories.json");
|
||||||
|
Product.__categoryNames = Object.keys(Product.categories);
|
||||||
|
/*
|
||||||
|
Product.categories = {
|
||||||
|
SOFTWARE: "SOFTWARE",
|
||||||
|
PHYSICAL_GOOD: "PHYSICAL_GOOD",
|
||||||
|
DIGITAL_MEDIA_BOOKS_MOVIES_MUSIC: "DIGITAL_MEDIA_BOOKS_MOVIES_MUSIC",
|
||||||
|
DIGITAL_GAMES: "DIGITAL_GAMES",
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
Product.create = async function _createSubscription({
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
type,
|
||||||
|
category,
|
||||||
|
image_url,
|
||||||
|
home_url,
|
||||||
|
}) {
|
||||||
|
if (id) {
|
||||||
|
if (!id.startsWith("PROD-")) {
|
||||||
|
console.warn(`Warn: product ID should start with "PROD-"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!Product.__typeNames.includes(type)) {
|
||||||
|
console.warn(`Warn: unknown product type '${type}'`);
|
||||||
|
}
|
||||||
|
if (!Product.__categoryNames.includes(category)) {
|
||||||
|
console.warn(`Warn: unknown product category '${category}'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await PayPal.request({
|
||||||
|
method: "POST",
|
||||||
|
url: "/v1/catalogs/products",
|
||||||
|
id: id,
|
||||||
|
json: {
|
||||||
|
// ex: "Video Streaming Service"
|
||||||
|
name: name,
|
||||||
|
// ex: "Video streaming service"
|
||||||
|
description: description,
|
||||||
|
// ex: "SERVICE", "PHYSICAL", "DIGITAL"
|
||||||
|
type: type,
|
||||||
|
// ex: "SOFTWARE", "PHYSICAL_GOOD"
|
||||||
|
category: category,
|
||||||
|
// ex: "https://example.com/streaming.jpg"
|
||||||
|
image_url: image_url,
|
||||||
|
// ex: "https://example.com/home"
|
||||||
|
home_url: home_url,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(must201or200)
|
||||||
|
.then(justBody);
|
||||||
|
};
|
||||||
|
|
||||||
|
let Plan = {};
|
||||||
|
Plan.intervals = {
|
||||||
|
DAY: "DAY",
|
||||||
|
WEEK: "WEEK",
|
||||||
|
MONTH: "MONTH",
|
||||||
|
YEAR: "YEAR",
|
||||||
|
};
|
||||||
|
Plan.tenures = {
|
||||||
|
TRIAL: "TRIAL",
|
||||||
|
REGULAR: "REGULAR",
|
||||||
|
};
|
||||||
|
|
||||||
|
// See https://developer.paypal.com/docs/api/subscriptions/v1/
|
||||||
|
Plan.create = async function _createPlan({
|
||||||
|
id,
|
||||||
|
status = "ACTIVE",
|
||||||
|
product_id,
|
||||||
|
name,
|
||||||
|
description = "",
|
||||||
|
billing_cycles,
|
||||||
|
payment_preferences,
|
||||||
|
taxes, // optional
|
||||||
|
quantity_supported = false,
|
||||||
|
}) {
|
||||||
|
let headers = {};
|
||||||
|
if (id) {
|
||||||
|
if (!id.startsWith("PLAN-")) {
|
||||||
|
// ex: PLAN-18062020-001
|
||||||
|
console.warn(`Warn: plan ID should start with "PLAN-"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
headers["Prefer"] = "return=representation";
|
||||||
|
return await PayPal.request({
|
||||||
|
method: "POST",
|
||||||
|
url: "/v1/billing/plans",
|
||||||
|
id: id,
|
||||||
|
headers: headers,
|
||||||
|
json: {
|
||||||
|
// ex: "PROD-6XB24663H4094933M"
|
||||||
|
product_id: product_id,
|
||||||
|
// ex: "Basic Plan"
|
||||||
|
name: name,
|
||||||
|
// ex: "Basic plan"
|
||||||
|
description: description,
|
||||||
|
// ex: "CREATED", "ACTIVE", "INACTIVE"
|
||||||
|
status: status,
|
||||||
|
// ex: TODO
|
||||||
|
billing_cycles: billing_cycles.map(function (cycle, i) {
|
||||||
|
// sequence is the index in the array,
|
||||||
|
// which should never be out-of-order
|
||||||
|
if (!cycle.frequency.interval_count) {
|
||||||
|
cycle.frequency.interval_count = 1;
|
||||||
|
}
|
||||||
|
cycle.sequence = i + 1;
|
||||||
|
if (!cycle.tenure_type) {
|
||||||
|
cycle.tenure_type = Plan.tenures.REGULAR;
|
||||||
|
}
|
||||||
|
if (!cycle.total_cycles) {
|
||||||
|
cycle.total_cycles = 0;
|
||||||
|
}
|
||||||
|
return cycle;
|
||||||
|
}),
|
||||||
|
// TODO ???
|
||||||
|
payment_preferences: payment_preferences,
|
||||||
|
taxes: taxes,
|
||||||
|
quantity_supported: quantity_supported,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(must201or200)
|
||||||
|
.then(justBody);
|
||||||
|
};
|
||||||
|
|
||||||
|
let Subscription = {};
|
||||||
|
Subscription.actions = {
|
||||||
|
CONTINUE: "CONTINUE",
|
||||||
|
SUBSCRIBE_NOW: "SUBSCRIBE_NOW",
|
||||||
|
};
|
||||||
|
Subscription.shipping_preferences = {
|
||||||
|
GET_FROM_FILE: "GET_FROM_FILE", // provided, or selectable from PayPal addresses
|
||||||
|
SET_PROVIDED_ADDRESS: "SET_PROVIDED_ADDRESS", // user can't change it here
|
||||||
|
NO_SHIPPING: "NO_SHIPPING", // duh
|
||||||
|
};
|
||||||
|
Subscription.payer_selections = {
|
||||||
|
PAYPAL: "PAYPAL",
|
||||||
|
};
|
||||||
|
Subscription.payee_preferences = {
|
||||||
|
UNRESTRICTED: "UNRESTRICTED",
|
||||||
|
IMMEDIATE_PAYMENT_REQUIRED: "IMMEDIATE_PAYMENT_REQUIRED",
|
||||||
|
};
|
||||||
|
|
||||||
|
Subscription.createRequest = async function _createSubscription({
|
||||||
|
id,
|
||||||
|
plan_id,
|
||||||
|
start_time,
|
||||||
|
quantity,
|
||||||
|
shipping_amount,
|
||||||
|
subscriber,
|
||||||
|
application_context,
|
||||||
|
}) {
|
||||||
|
return await PayPal.request({
|
||||||
|
method: "POST",
|
||||||
|
url: "/v1/billing/subscriptions",
|
||||||
|
id: id,
|
||||||
|
json: {
|
||||||
|
// ex: "P-5ML4271244454362WXNWU5NQ"
|
||||||
|
plan_id: plan_id,
|
||||||
|
// ex: "2018-11-01T00:00:00Z" (must be in the future)
|
||||||
|
start_time: start_time,
|
||||||
|
// ex: "20"
|
||||||
|
quantity: quantity,
|
||||||
|
// ex: { currency_code: "USD", value: "10.00", },
|
||||||
|
shipping_amount: shipping_amount,
|
||||||
|
/* ex:
|
||||||
|
{
|
||||||
|
name: { given_name: "John", surname: "Doe" },
|
||||||
|
email_address: "customer@example.com",
|
||||||
|
shipping_address: {
|
||||||
|
name: { full_name: "John 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",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
subscriber: subscriber,
|
||||||
|
/* ex:
|
||||||
|
{
|
||||||
|
brand_name: "walmart",
|
||||||
|
locale: "en-US",
|
||||||
|
shipping_preference: "SET_PROVIDED_ADDRESS",
|
||||||
|
user_action: "SUBSCRIBE_NOW",
|
||||||
|
payment_method: {
|
||||||
|
payer_selected: "PAYPAL",
|
||||||
|
payee_preferred: "IMMEDIATE_PAYMENT_REQUIRED",
|
||||||
|
},
|
||||||
|
return_url: "https://example.com/returnUrl",
|
||||||
|
cancel_url: "https://example.com/cancelUrl",
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
application_context: application_context,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(must201or200)
|
||||||
|
.then(justBody);
|
||||||
|
};
|
||||||
|
|
||||||
|
Subscription.get = async function _getSubscription(id) {
|
||||||
|
return await PayPal.request({
|
||||||
|
url: `/v1/billing/subscriptions/${id}`,
|
||||||
|
json: true,
|
||||||
|
})
|
||||||
|
.then(must201or200)
|
||||||
|
.then(justBody);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.init = PayPal.init;
|
||||||
|
module.exports.request = PayPal.request;
|
||||||
|
module.exports.Plan = Plan;
|
||||||
|
module.exports.Product = Product;
|
||||||
|
module.exports.Subscription = Subscription;
|
156
test.js
Normal file
156
test.js
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
require("dotenv").config({ path: ".env" });
|
||||||
|
require("dotenv").config({ path: ".env.secret" });
|
||||||
|
|
||||||
|
if (!process.env.PAYPAL_CLIENT_ID) {
|
||||||
|
console.error(
|
||||||
|
"Please copy example.env to .env and update the values from the PayPal API Dashboard at https://developer.paypal.com/developer/applications"
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let PayPal = require("./");
|
||||||
|
let { Plan, Product, Subscription } = PayPal;
|
||||||
|
|
||||||
|
async function test() {
|
||||||
|
console.info();
|
||||||
|
|
||||||
|
let product = await Product.create({
|
||||||
|
id: "PROD-test-product-10",
|
||||||
|
name: "Test Product #10",
|
||||||
|
description: "A great widget for gizmos and gadgets of all ages!",
|
||||||
|
type: Product.types.SERVICE,
|
||||||
|
category: Product.categories.SOFTWARE,
|
||||||
|
image_url: undefined,
|
||||||
|
home_url: undefined,
|
||||||
|
});
|
||||||
|
console.info("Product:");
|
||||||
|
console.info(JSON.stringify(product, null, 2));
|
||||||
|
console.info();
|
||||||
|
|
||||||
|
let plan = await Plan.create({
|
||||||
|
id: "PLAN-test-plan-001",
|
||||||
|
product_id: "PROD-2TS60422HM5801517", // product.id,
|
||||||
|
name: "Test Plan #1",
|
||||||
|
description: "A great plan for pros of all ages!",
|
||||||
|
billing_cycles: [
|
||||||
|
{
|
||||||
|
frequency: {
|
||||||
|
interval_unit: Plan.intervals.DAY,
|
||||||
|
interval_count: 1,
|
||||||
|
},
|
||||||
|
tenure_type: Plan.tenures.TRIAL,
|
||||||
|
total_cycles: 14,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
frequency: {
|
||||||
|
interval_unit: Plan.intervals.YEAR,
|
||||||
|
interval_count: 1,
|
||||||
|
},
|
||||||
|
tenure_type: Plan.tenures.REGULAR,
|
||||||
|
total_cycles: 0,
|
||||||
|
pricing_scheme: {
|
||||||
|
fixed_price: {
|
||||||
|
value: "10.00",
|
||||||
|
currency_code: "USD",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
payment_preferences: {
|
||||||
|
auto_bill_outstanding: true,
|
||||||
|
setup_fee: {
|
||||||
|
value: "10",
|
||||||
|
currency_code: "USD",
|
||||||
|
},
|
||||||
|
setup_fee_failure_action: "CONTINUE",
|
||||||
|
// suspend the subscription after N attempts
|
||||||
|
payment_failure_threshold: 3,
|
||||||
|
},
|
||||||
|
taxes: {
|
||||||
|
percentage: "10",
|
||||||
|
// was tax included?
|
||||||
|
inclusive: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.info("Plan:");
|
||||||
|
console.info(JSON.stringify(plan, null, 2));
|
||||||
|
console.info();
|
||||||
|
|
||||||
|
let subscription = await Subscription.createRequest({
|
||||||
|
// See https://developer.paypal.com/docs/subscriptions/integrate/#use-the-subscriptions-api
|
||||||
|
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: "root",
|
||||||
|
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();
|
||||||
|
|
||||||
|
// wait for user to click URL and accept
|
||||||
|
await new Promise(function (resolve) {
|
||||||
|
console.info();
|
||||||
|
console.info("Please approve the subscription at the following URL:");
|
||||||
|
console.info();
|
||||||
|
console.info(
|
||||||
|
"Approve URL:",
|
||||||
|
subscription.links.find(function (link) {
|
||||||
|
return "approve" === link.rel;
|
||||||
|
}).href
|
||||||
|
);
|
||||||
|
console.info("Username:", process.env.PAYPAL_SANDBOX_EMAIL);
|
||||||
|
console.info("Password:", process.env.PAYPAL_SANDBOX_PASSWORD);
|
||||||
|
console.info();
|
||||||
|
console.info("Did you approve it? Hit the <any> key to continue...");
|
||||||
|
console.info();
|
||||||
|
process.stdin.once("data", resolve);
|
||||||
|
});
|
||||||
|
process.stdin.pause();
|
||||||
|
|
||||||
|
let s = await Subscription.get(subscription.id);
|
||||||
|
console.info("Subscription: (After Approval)");
|
||||||
|
console.info(JSON.stringify(s, null, 2));
|
||||||
|
console.info();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
PayPal.init(process.env.PAYPAL_CLIENT_ID, process.env.PAYPAL_CLIENT_SECRET);
|
||||||
|
test().catch(function (err) {
|
||||||
|
console.error("Something bad happened:");
|
||||||
|
console.error(JSON.stringify(err, null, 2));
|
||||||
|
});
|
||||||
|
}
|
475
utils/make-categories.js
Normal file
475
utils/make-categories.js
Normal file
@ -0,0 +1,475 @@
|
|||||||
|
// Documented under "categories" at
|
||||||
|
// https://developer.paypal.com/docs/api/catalog-products/v1/
|
||||||
|
/*
|
||||||
|
// To scrape the full list from the site:
|
||||||
|
var categories = [];
|
||||||
|
var $c = $$('.dax-def-label code').find(function ($el) {
|
||||||
|
if ("category" === $el.innerText.toLowerCase()) {
|
||||||
|
return $el;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$c.closest('div').nextElementSibling.querySelectorAll('li').forEach(function ($el) {
|
||||||
|
categories.push($el.querySelector('code').innerText);
|
||||||
|
});
|
||||||
|
console.log(JSON.stringify(categories));
|
||||||
|
*/
|
||||||
|
let categories = [
|
||||||
|
"AC_REFRIGERATION_REPAIR",
|
||||||
|
"ACADEMIC_SOFTWARE",
|
||||||
|
"ACCESSORIES",
|
||||||
|
"ACCOUNTING",
|
||||||
|
"ADULT",
|
||||||
|
"ADVERTISING",
|
||||||
|
"AFFILIATED_AUTO_RENTAL",
|
||||||
|
"AGENCIES",
|
||||||
|
"AGGREGATORS",
|
||||||
|
"AGRICULTURAL_COOPERATIVE_FOR_MAIL_ORDER",
|
||||||
|
"AIR_CARRIERS_AIRLINES",
|
||||||
|
"AIRLINES",
|
||||||
|
"AIRPORTS_FLYING_FIELDS",
|
||||||
|
"ALCOHOLIC_BEVERAGES",
|
||||||
|
"AMUSEMENT_PARKS_CARNIVALS",
|
||||||
|
"ANIMATION",
|
||||||
|
"ANTIQUES",
|
||||||
|
"APPLIANCES",
|
||||||
|
"AQUARIAMS_SEAQUARIUMS_DOLPHINARIUMS",
|
||||||
|
"ARCHITECTURAL_ENGINEERING_AND_SURVEYING_SERVICES",
|
||||||
|
"ART_AND_CRAFT_SUPPLIES",
|
||||||
|
"ART_DEALERS_AND_GALLERIES",
|
||||||
|
"ARTIFACTS_GRAVE_RELATED_AND_NATIVE_AMERICAN_CRAFTS",
|
||||||
|
"ARTS_AND_CRAFTS",
|
||||||
|
"ARTS_CRAFTS_AND_COLLECTIBLES",
|
||||||
|
"AUDIO_BOOKS",
|
||||||
|
"AUTO_ASSOCIATIONS_CLUBS",
|
||||||
|
"AUTO_DEALER_USED_ONLY",
|
||||||
|
"AUTO_RENTALS",
|
||||||
|
"AUTO_SERVICE",
|
||||||
|
"AUTOMATED_FUEL_DISPENSERS",
|
||||||
|
"AUTOMOBILE_ASSOCIATIONS",
|
||||||
|
"AUTOMOTIVE",
|
||||||
|
"AUTOMOTIVE_REPAIR_SHOPS_NON_DEALER",
|
||||||
|
"AUTOMOTIVE_TOP_AND_BODY_SHOPS",
|
||||||
|
"AVIATION",
|
||||||
|
"BABIES_CLOTHING_AND_SUPPLIES",
|
||||||
|
"BABY",
|
||||||
|
"BANDS_ORCHESTRAS_ENTERTAINERS",
|
||||||
|
"BARBIES",
|
||||||
|
"BATH_AND_BODY",
|
||||||
|
"BATTERIES",
|
||||||
|
"BEAN_BABIES",
|
||||||
|
"BEAUTY",
|
||||||
|
"BEAUTY_AND_FRAGRANCES",
|
||||||
|
"BED_AND_BATH",
|
||||||
|
"BICYCLE_SHOPS_SALES_AND_SERVICE",
|
||||||
|
"BICYCLES_AND_ACCESSORIES",
|
||||||
|
"BILLIARD_POOL_ESTABLISHMENTS",
|
||||||
|
"BOAT_DEALERS",
|
||||||
|
"BOAT_RENTALS_AND_LEASING",
|
||||||
|
"BOATING_SAILING_AND_ACCESSORIES",
|
||||||
|
"BOOKS",
|
||||||
|
"BOOKS_AND_MAGAZINES",
|
||||||
|
"BOOKS_MANUSCRIPTS",
|
||||||
|
"BOOKS_PERIODICALS_AND_NEWSPAPERS",
|
||||||
|
"BOWLING_ALLEYS",
|
||||||
|
"BULLETIN_BOARD",
|
||||||
|
"BUS_LINE",
|
||||||
|
"BUS_LINES_CHARTERS_TOUR_BUSES",
|
||||||
|
"BUSINESS",
|
||||||
|
"BUSINESS_AND_SECRETARIAL_SCHOOLS",
|
||||||
|
"BUYING_AND_SHOPPING_SERVICES_AND_CLUBS",
|
||||||
|
"CABLE_SATELLITE_AND_OTHER_PAY_TELEVISION_AND_RADIO_SERVICES",
|
||||||
|
"CABLE_SATELLITE_AND_OTHER_PAY_TV_AND_RADIO",
|
||||||
|
"CAMERA_AND_PHOTOGRAPHIC_SUPPLIES",
|
||||||
|
"CAMERAS",
|
||||||
|
"CAMERAS_AND_PHOTOGRAPHY",
|
||||||
|
"CAMPER_RECREATIONAL_AND_UTILITY_TRAILER_DEALERS",
|
||||||
|
"CAMPING_AND_OUTDOORS",
|
||||||
|
"CAMPING_AND_SURVIVAL",
|
||||||
|
"CAR_AND_TRUCK_DEALERS",
|
||||||
|
"CAR_AND_TRUCK_DEALERS_USED_ONLY",
|
||||||
|
"CAR_AUDIO_AND_ELECTRONICS",
|
||||||
|
"CAR_RENTAL_AGENCY",
|
||||||
|
"CATALOG_MERCHANT",
|
||||||
|
"CATALOG_RETAIL_MERCHANT",
|
||||||
|
"CATERING_SERVICES",
|
||||||
|
"CHARITY",
|
||||||
|
"CHECK_CASHIER",
|
||||||
|
"CHILD_CARE_SERVICES",
|
||||||
|
"CHILDREN_BOOKS",
|
||||||
|
"CHIROPODISTS_PODIATRISTS",
|
||||||
|
"CHIROPRACTORS",
|
||||||
|
"CIGAR_STORES_AND_STANDS",
|
||||||
|
"CIVIC_SOCIAL_FRATERNAL_ASSOCIATIONS",
|
||||||
|
"CIVIL_SOCIAL_FRAT_ASSOCIATIONS",
|
||||||
|
"CLOTHING",
|
||||||
|
"CLOTHING_ACCESSORIES_AND_SHOES",
|
||||||
|
"CLOTHING_RENTAL",
|
||||||
|
"COFFEE_AND_TEA",
|
||||||
|
"COIN_OPERATED_BANKS_AND_CASINOS",
|
||||||
|
"COLLECTIBLES",
|
||||||
|
"COLLECTION_AGENCY",
|
||||||
|
"COLLEGES_AND_UNIVERSITIES",
|
||||||
|
"COMMERCIAL_EQUIPMENT",
|
||||||
|
"COMMERCIAL_FOOTWEAR",
|
||||||
|
"COMMERCIAL_PHOTOGRAPHY",
|
||||||
|
"COMMERCIAL_PHOTOGRAPHY_ART_AND_GRAPHICS",
|
||||||
|
"COMMERCIAL_SPORTS_PROFESSIONA",
|
||||||
|
"COMMODITIES_AND_FUTURES_EXCHANGE",
|
||||||
|
"COMPUTER_AND_DATA_PROCESSING_SERVICES",
|
||||||
|
"COMPUTER_HARDWARE_AND_SOFTWARE",
|
||||||
|
"COMPUTER_MAINTENANCE_REPAIR_AND_SERVICES_NOT_ELSEWHERE_CLAS",
|
||||||
|
"CONSTRUCTION",
|
||||||
|
"CONSTRUCTION_MATERIALS_NOT_ELSEWHERE_CLASSIFIED",
|
||||||
|
"CONSULTING_SERVICES",
|
||||||
|
"CONSUMER_CREDIT_REPORTING_AGENCIES",
|
||||||
|
"CONVALESCENT_HOMES",
|
||||||
|
"COSMETIC_STORES",
|
||||||
|
"COUNSELING_SERVICES_DEBT_MARRIAGE_PERSONAL",
|
||||||
|
"COUNTERFEIT_CURRENCY_AND_STAMPS",
|
||||||
|
"COUNTERFEIT_ITEMS",
|
||||||
|
"COUNTRY_CLUBS",
|
||||||
|
"COURIER_SERVICES",
|
||||||
|
"COURIER_SERVICES_AIR_AND_GROUND_AND_FREIGHT_FORWARDERS",
|
||||||
|
"COURT_COSTS_ALIMNY_CHILD_SUPT",
|
||||||
|
"COURT_COSTS_INCLUDING_ALIMONY_AND_CHILD_SUPPORT_COURTS_OF_LAW",
|
||||||
|
"CREDIT_CARDS",
|
||||||
|
"CREDIT_UNION",
|
||||||
|
"CULTURE_AND_RELIGION",
|
||||||
|
"DAIRY_PRODUCTS_STORES",
|
||||||
|
"DANCE_HALLS_STUDIOS_AND_SCHOOLS",
|
||||||
|
"DECORATIVE",
|
||||||
|
"DENTAL",
|
||||||
|
"DENTISTS_AND_ORTHODONTISTS",
|
||||||
|
"DEPARTMENT_STORES",
|
||||||
|
"DESKTOP_PCS",
|
||||||
|
"DEVICES",
|
||||||
|
"DIECAST_TOYS_VEHICLES",
|
||||||
|
"DIGITAL_GAMES",
|
||||||
|
"DIGITAL_MEDIA_BOOKS_MOVIES_MUSIC",
|
||||||
|
"DIRECT_MARKETING",
|
||||||
|
"DIRECT_MARKETING_CATALOG_MERCHANT",
|
||||||
|
"DIRECT_MARKETING_INBOUND_TELE",
|
||||||
|
"DIRECT_MARKETING_OUTBOUND_TELE",
|
||||||
|
"DIRECT_MARKETING_SUBSCRIPTION",
|
||||||
|
"DISCOUNT_STORES",
|
||||||
|
"DOOR_TO_DOOR_SALES",
|
||||||
|
"DRAPERY_WINDOW_COVERING_AND_UPHOLSTERY",
|
||||||
|
"DRINKING_PLACES",
|
||||||
|
"DRUGSTORE",
|
||||||
|
"DURABLE_GOODS",
|
||||||
|
"ECOMMERCE_DEVELOPMENT",
|
||||||
|
"ECOMMERCE_SERVICES",
|
||||||
|
"EDUCATIONAL_AND_TEXTBOOKS",
|
||||||
|
"ELECTRIC_RAZOR_STORES",
|
||||||
|
"ELECTRICAL_AND_SMALL_APPLIANCE_REPAIR",
|
||||||
|
"ELECTRICAL_CONTRACTORS",
|
||||||
|
"ELECTRICAL_PARTS_AND_EQUIPMENT",
|
||||||
|
"ELECTRONIC_CASH",
|
||||||
|
"ELEMENTARY_AND_SECONDARY_SCHOOLS",
|
||||||
|
"EMPLOYMENT",
|
||||||
|
"ENTERTAINERS",
|
||||||
|
"ENTERTAINMENT_AND_MEDIA",
|
||||||
|
"EQUIP_TOOL_FURNITURE_AND_APPLIANCE_RENTAL_AND_LEASING",
|
||||||
|
"ESCROW",
|
||||||
|
"EVENT_AND_WEDDING_PLANNING",
|
||||||
|
"EXERCISE_AND_FITNESS",
|
||||||
|
"EXERCISE_EQUIPMENT",
|
||||||
|
"EXTERMINATING_AND_DISINFECTING_SERVICES",
|
||||||
|
"FABRICS_AND_SEWING",
|
||||||
|
"FAMILY_CLOTHING_STORES",
|
||||||
|
"FASHION_JEWELRY",
|
||||||
|
"FAST_FOOD_RESTAURANTS",
|
||||||
|
"FICTION_AND_NONFICTION",
|
||||||
|
"FINANCE_COMPANY",
|
||||||
|
"FINANCIAL_AND_INVESTMENT_ADVICE",
|
||||||
|
"FINANCIAL_INSTITUTIONS_MERCHANDISE_AND_SERVICES",
|
||||||
|
"FIREARM_ACCESSORIES",
|
||||||
|
"FIREARMS_WEAPONS_AND_KNIVES",
|
||||||
|
"FIREPLACE_AND_FIREPLACE_SCREENS",
|
||||||
|
"FIREWORKS",
|
||||||
|
"FISHING",
|
||||||
|
"FLORISTS",
|
||||||
|
"FLOWERS",
|
||||||
|
"FOOD_DRINK_AND_NUTRITION",
|
||||||
|
"FOOD_PRODUCTS",
|
||||||
|
"FOOD_RETAIL_AND_SERVICE",
|
||||||
|
"FRAGRANCES_AND_PERFUMES",
|
||||||
|
"FREEZER_AND_LOCKER_MEAT_PROVISIONERS",
|
||||||
|
"FUEL_DEALERS_FUEL_OIL_WOOD_AND_COAL",
|
||||||
|
"FUEL_DEALERS_NON_AUTOMOTIVE",
|
||||||
|
"FUNERAL_SERVICES_AND_CREMATORIES",
|
||||||
|
"FURNISHING_AND_DECORATING",
|
||||||
|
"FURNITURE",
|
||||||
|
"FURRIERS_AND_FUR_SHOPS",
|
||||||
|
"GADGETS_AND_OTHER_ELECTRONICS",
|
||||||
|
"GAMBLING",
|
||||||
|
"GAME_SOFTWARE",
|
||||||
|
"GAMES",
|
||||||
|
"GARDEN_SUPPLIES",
|
||||||
|
"GENERAL",
|
||||||
|
"GENERAL_CONTRACTORS",
|
||||||
|
"GENERAL_GOVERNMENT",
|
||||||
|
"GENERAL_SOFTWARE",
|
||||||
|
"GENERAL_TELECOM",
|
||||||
|
"GIFTS_AND_FLOWERS",
|
||||||
|
"GLASS_PAINT_AND_WALLPAPER_STORES",
|
||||||
|
"GLASSWARE_CRYSTAL_STORES",
|
||||||
|
"GOVERNMENT",
|
||||||
|
"GOVERNMENT_IDS_AND_LICENSES",
|
||||||
|
"GOVERNMENT_LICENSED_ON_LINE_CASINOS_ON_LINE_GAMBLING",
|
||||||
|
"GOVERNMENT_OWNED_LOTTERIES",
|
||||||
|
"GOVERNMENT_SERVICES",
|
||||||
|
"GRAPHIC_AND_COMMERCIAL_DESIGN",
|
||||||
|
"GREETING_CARDS",
|
||||||
|
"GROCERY_STORES_AND_SUPERMARKETS",
|
||||||
|
"HARDWARE_AND_TOOLS",
|
||||||
|
"HARDWARE_EQUIPMENT_AND_SUPPLIES",
|
||||||
|
"HAZARDOUS_RESTRICTED_AND_PERISHABLE_ITEMS",
|
||||||
|
"HEALTH_AND_BEAUTY_SPAS",
|
||||||
|
"HEALTH_AND_NUTRITION",
|
||||||
|
"HEALTH_AND_PERSONAL_CARE",
|
||||||
|
"HEARING_AIDS_SALES_AND_SUPPLIES",
|
||||||
|
"HEATING_PLUMBING_AC",
|
||||||
|
"HIGH_RISK_MERCHANT",
|
||||||
|
"HIRING_SERVICES",
|
||||||
|
"HOBBIES_TOYS_AND_GAMES",
|
||||||
|
"HOME_AND_GARDEN",
|
||||||
|
"HOME_AUDIO",
|
||||||
|
"HOME_DECOR",
|
||||||
|
"HOME_ELECTRONICS",
|
||||||
|
"HOSPITALS",
|
||||||
|
"HOTELS_MOTELS_INNS_RESORTS",
|
||||||
|
"HOUSEWARES",
|
||||||
|
"HUMAN_PARTS_AND_REMAINS",
|
||||||
|
"HUMOROUS_GIFTS_AND_NOVELTIES",
|
||||||
|
"HUNTING",
|
||||||
|
"IDS_LICENSES_AND_PASSPORTS",
|
||||||
|
"ILLEGAL_DRUGS_AND_PARAPHERNALIA",
|
||||||
|
"INDUSTRIAL",
|
||||||
|
"INDUSTRIAL_AND_MANUFACTURING_SUPPLIES",
|
||||||
|
"INSURANCE_AUTO_AND_HOME",
|
||||||
|
"INSURANCE_DIRECT",
|
||||||
|
"INSURANCE_LIFE_AND_ANNUITY",
|
||||||
|
"INSURANCE_SALES_UNDERWRITING",
|
||||||
|
"INSURANCE_UNDERWRITING_PREMIUMS",
|
||||||
|
"INTERNET_AND_NETWORK_SERVICES",
|
||||||
|
"INTRA_COMPANY_PURCHASES",
|
||||||
|
"LABORATORIES_DENTAL_MEDICAL",
|
||||||
|
"LANDSCAPING",
|
||||||
|
"LANDSCAPING_AND_HORTICULTURAL_SERVICES",
|
||||||
|
"LAUNDRY_CLEANING_SERVICES",
|
||||||
|
"LEGAL",
|
||||||
|
"LEGAL_SERVICES_AND_ATTORNEYS",
|
||||||
|
"LOCAL_DELIVERY_SERVICE",
|
||||||
|
"LOCKSMITH",
|
||||||
|
"LODGING_AND_ACCOMMODATIONS",
|
||||||
|
"LOTTERY_AND_CONTESTS",
|
||||||
|
"LUGGAGE_AND_LEATHER_GOODS",
|
||||||
|
"LUMBER_AND_BUILDING_MATERIALS",
|
||||||
|
"MAGAZINES",
|
||||||
|
"MAINTENANCE_AND_REPAIR_SERVICES",
|
||||||
|
"MAKEUP_AND_COSMETICS",
|
||||||
|
"MANUAL_CASH_DISBURSEMENTS",
|
||||||
|
"MASSAGE_PARLORS",
|
||||||
|
"MEDICAL",
|
||||||
|
"MEDICAL_AND_PHARMACEUTICAL",
|
||||||
|
"MEDICAL_CARE",
|
||||||
|
"MEDICAL_EQUIPMENT_AND_SUPPLIES",
|
||||||
|
"MEDICAL_SERVICES",
|
||||||
|
"MEETING_PLANNERS",
|
||||||
|
"MEMBERSHIP_CLUBS_AND_ORGANIZATIONS",
|
||||||
|
"MEMBERSHIP_COUNTRY_CLUBS_GOLF",
|
||||||
|
"MEMORABILIA",
|
||||||
|
"MEN_AND_BOY_CLOTHING_AND_ACCESSORY_STORES",
|
||||||
|
"MEN_CLOTHING",
|
||||||
|
"MERCHANDISE",
|
||||||
|
"METAPHYSICAL",
|
||||||
|
"MILITARIA",
|
||||||
|
"MILITARY_AND_CIVIL_SERVICE_UNIFORMS",
|
||||||
|
"MISC._AUTOMOTIVE_AIRCRAFT_AND_FARM_EQUIPMENT_DEALERS",
|
||||||
|
"MISC._GENERAL_MERCHANDISE",
|
||||||
|
"MISCELLANEOUS_GENERAL_SERVICES",
|
||||||
|
"MISCELLANEOUS_REPAIR_SHOPS_AND_RELATED_SERVICES",
|
||||||
|
"MODEL_KITS",
|
||||||
|
"MONEY_TRANSFER_MEMBER_FINANCIAL_INSTITUTION",
|
||||||
|
"MONEY_TRANSFER_MERCHANT",
|
||||||
|
"MOTION_PICTURE_THEATERS",
|
||||||
|
"MOTOR_FREIGHT_CARRIERS_AND_TRUCKING",
|
||||||
|
"MOTOR_HOME_AND_RECREATIONAL_VEHICLE_RENTAL",
|
||||||
|
"MOTOR_HOMES_DEALERS",
|
||||||
|
"MOTOR_VEHICLE_SUPPLIES_AND_NEW_PARTS",
|
||||||
|
"MOTORCYCLE_DEALERS",
|
||||||
|
"MOTORCYCLES",
|
||||||
|
"MOVIE",
|
||||||
|
"MOVIE_TICKETS",
|
||||||
|
"MOVING_AND_STORAGE",
|
||||||
|
"MULTI_LEVEL_MARKETING",
|
||||||
|
"MUSIC_CDS_CASSETTES_AND_ALBUMS",
|
||||||
|
"MUSIC_STORE_INSTRUMENTS_AND_SHEET_MUSIC",
|
||||||
|
"NETWORKING",
|
||||||
|
"NEW_AGE",
|
||||||
|
"NEW_PARTS_AND_SUPPLIES_MOTOR_VEHICLE",
|
||||||
|
"NEWS_DEALERS_AND_NEWSTANDS",
|
||||||
|
"NON_DURABLE_GOODS",
|
||||||
|
"NON_FICTION",
|
||||||
|
"NON_PROFIT_POLITICAL_AND_RELIGION",
|
||||||
|
"NONPROFIT",
|
||||||
|
"NOVELTIES",
|
||||||
|
"OEM_SOFTWARE",
|
||||||
|
"OFFICE_SUPPLIES_AND_EQUIPMENT",
|
||||||
|
"ONLINE_DATING",
|
||||||
|
"ONLINE_GAMING",
|
||||||
|
"ONLINE_GAMING_CURRENCY",
|
||||||
|
"ONLINE_SERVICES",
|
||||||
|
"OOUTBOUND_TELEMARKETING_MERCH",
|
||||||
|
"OPHTHALMOLOGISTS_OPTOMETRIST",
|
||||||
|
"OPTICIANS_AND_DISPENSING",
|
||||||
|
"ORTHOPEDIC_GOODS_PROSTHETICS",
|
||||||
|
"OSTEOPATHS",
|
||||||
|
"OTHER",
|
||||||
|
"PACKAGE_TOUR_OPERATORS",
|
||||||
|
"PAINTBALL",
|
||||||
|
"PAINTS_VARNISHES_AND_SUPPLIES",
|
||||||
|
"PARKING_LOTS_AND_GARAGES",
|
||||||
|
"PARTS_AND_ACCESSORIES",
|
||||||
|
"PAWN_SHOPS",
|
||||||
|
"PAYCHECK_LENDER_OR_CASH_ADVANCE",
|
||||||
|
"PERIPHERALS",
|
||||||
|
"PERSONALIZED_GIFTS",
|
||||||
|
"PET_SHOPS_PET_FOOD_AND_SUPPLIES",
|
||||||
|
"PETROLEUM_AND_PETROLEUM_PRODUCTS",
|
||||||
|
"PETS_AND_ANIMALS",
|
||||||
|
"PHOTOFINISHING_LABORATORIES_PHOTO_DEVELOPING",
|
||||||
|
"PHOTOGRAPHIC_STUDIOS_PORTRAITS",
|
||||||
|
"PHOTOGRAPHY",
|
||||||
|
"PHYSICAL_GOOD",
|
||||||
|
"PICTURE_VIDEO_PRODUCTION",
|
||||||
|
"PIECE_GOODS_NOTIONS_AND_OTHER_DRY_GOODS",
|
||||||
|
"PLANTS_AND_SEEDS",
|
||||||
|
"PLUMBING_AND_HEATING_EQUIPMENTS_AND_SUPPLIES",
|
||||||
|
"POLICE_RELATED_ITEMS",
|
||||||
|
"POLITICAL_ORGANIZATIONS",
|
||||||
|
"POSTAL_SERVICES_GOVERNMENT_ONLY",
|
||||||
|
"POSTERS",
|
||||||
|
"PREPAID_AND_STORED_VALUE_CARDS",
|
||||||
|
"PRESCRIPTION_DRUGS",
|
||||||
|
"PROMOTIONAL_ITEMS",
|
||||||
|
"PUBLIC_WAREHOUSING_AND_STORAGE",
|
||||||
|
"PUBLISHING_AND_PRINTING",
|
||||||
|
"PUBLISHING_SERVICES",
|
||||||
|
"RADAR_DECTORS",
|
||||||
|
"RADIO_TELEVISION_AND_STEREO_REPAIR",
|
||||||
|
"REAL_ESTATE",
|
||||||
|
"REAL_ESTATE_AGENT",
|
||||||
|
"REAL_ESTATE_AGENTS_AND_MANAGERS_RENTALS",
|
||||||
|
"RELIGION_AND_SPIRITUALITY_FOR_PROFIT",
|
||||||
|
"RELIGIOUS",
|
||||||
|
"RELIGIOUS_ORGANIZATIONS",
|
||||||
|
"REMITTANCE",
|
||||||
|
"RENTAL_PROPERTY_MANAGEMENT",
|
||||||
|
"RESIDENTIAL",
|
||||||
|
"RETAIL",
|
||||||
|
"RETAIL_FINE_JEWELRY_AND_WATCHES",
|
||||||
|
"REUPHOLSTERY_AND_FURNITURE_REPAIR",
|
||||||
|
"RINGS",
|
||||||
|
"ROOFING_SIDING_SHEET_METAL",
|
||||||
|
"RUGS_AND_CARPETS",
|
||||||
|
"SCHOOLS_AND_COLLEGES",
|
||||||
|
"SCIENCE_FICTION",
|
||||||
|
"SCRAPBOOKING",
|
||||||
|
"SCULPTURES",
|
||||||
|
"SECURITIES_BROKERS_AND_DEALERS",
|
||||||
|
"SECURITY_AND_SURVEILLANCE",
|
||||||
|
"SECURITY_AND_SURVEILLANCE_EQUIPMENT",
|
||||||
|
"SECURITY_BROKERS_AND_DEALERS",
|
||||||
|
"SEMINARS",
|
||||||
|
"SERVICE_STATIONS",
|
||||||
|
"SERVICES",
|
||||||
|
"SEWING_NEEDLEWORK_FABRIC_AND_PIECE_GOODS_STORES",
|
||||||
|
"SHIPPING_AND_PACKING",
|
||||||
|
"SHOE_REPAIR_HAT_CLEANING",
|
||||||
|
"SHOE_STORES",
|
||||||
|
"SHOES",
|
||||||
|
"SNOWMOBILE_DEALERS",
|
||||||
|
"SOFTWARE",
|
||||||
|
"SPECIALTY_AND_MISC._FOOD_STORES",
|
||||||
|
"SPECIALTY_CLEANING_POLISHING_AND_SANITATION_PREPARATIONS",
|
||||||
|
"SPECIALTY_OR_RARE_PETS",
|
||||||
|
"SPORT_GAMES_AND_TOYS",
|
||||||
|
"SPORTING_AND_RECREATIONAL_CAMPS",
|
||||||
|
"SPORTING_GOODS",
|
||||||
|
"SPORTS_AND_OUTDOORS",
|
||||||
|
"SPORTS_AND_RECREATION",
|
||||||
|
"STAMP_AND_COIN",
|
||||||
|
"STATIONARY_PRINTING_AND_WRITING_PAPER",
|
||||||
|
"STENOGRAPHIC_AND_SECRETARIAL_SUPPORT_SERVICES",
|
||||||
|
"STOCKS_BONDS_SECURITIES_AND_RELATED_CERTIFICATES",
|
||||||
|
"STORED_VALUE_CARDS",
|
||||||
|
"SUPPLIES",
|
||||||
|
"SUPPLIES_AND_TOYS",
|
||||||
|
"SURVEILLANCE_EQUIPMENT",
|
||||||
|
"SWIMMING_POOLS_AND_SPAS",
|
||||||
|
"SWIMMING_POOLS_SALES_SUPPLIES_SERVICES",
|
||||||
|
"TAILORS_AND_ALTERATIONS",
|
||||||
|
"TAX_PAYMENTS",
|
||||||
|
"TAX_PAYMENTS_GOVERNMENT_AGENCIES",
|
||||||
|
"TAXICABS_AND_LIMOUSINES",
|
||||||
|
"TELECOMMUNICATION_SERVICES",
|
||||||
|
"TELEPHONE_CARDS",
|
||||||
|
"TELEPHONE_EQUIPMENT",
|
||||||
|
"TELEPHONE_SERVICES",
|
||||||
|
"THEATER",
|
||||||
|
"TIRE_RETREADING_AND_REPAIR",
|
||||||
|
"TOLL_OR_BRIDGE_FEES",
|
||||||
|
"TOOLS_AND_EQUIPMENT",
|
||||||
|
"TOURIST_ATTRACTIONS_AND_EXHIBITS",
|
||||||
|
"TOWING_SERVICE",
|
||||||
|
"TOYS_AND_GAMES",
|
||||||
|
"TRADE_AND_VOCATIONAL_SCHOOLS",
|
||||||
|
"TRADEMARK_INFRINGEMENT",
|
||||||
|
"TRAILER_PARKS_AND_CAMPGROUNDS",
|
||||||
|
"TRAINING_SERVICES",
|
||||||
|
"TRANSPORTATION_SERVICES",
|
||||||
|
"TRAVEL",
|
||||||
|
"TRUCK_AND_UTILITY_TRAILER_RENTALS",
|
||||||
|
"TRUCK_STOP",
|
||||||
|
"TYPESETTING_PLATE_MAKING_AND_RELATED_SERVICES",
|
||||||
|
"USED_MERCHANDISE_AND_SECONDHAND_STORES",
|
||||||
|
"USED_PARTS_MOTOR_VEHICLE",
|
||||||
|
"UTILITIES",
|
||||||
|
"UTILITIES_ELECTRIC_GAS_WATER_SANITARY",
|
||||||
|
"VARIETY_STORES",
|
||||||
|
"VEHICLE_SALES",
|
||||||
|
"VEHICLE_SERVICE_AND_ACCESSORIES",
|
||||||
|
"VIDEO_EQUIPMENT",
|
||||||
|
"VIDEO_GAME_ARCADES_ESTABLISH",
|
||||||
|
"VIDEO_GAMES_AND_SYSTEMS",
|
||||||
|
"VIDEO_TAPE_RENTAL_STORES",
|
||||||
|
"VINTAGE_AND_COLLECTIBLE_VEHICLES",
|
||||||
|
"VINTAGE_AND_COLLECTIBLES",
|
||||||
|
"VITAMINS_AND_SUPPLEMENTS",
|
||||||
|
"VOCATIONAL_AND_TRADE_SCHOOLS",
|
||||||
|
"WATCH_CLOCK_AND_JEWELRY_REPAIR",
|
||||||
|
"WEB_HOSTING_AND_DESIGN",
|
||||||
|
"WELDING_REPAIR",
|
||||||
|
"WHOLESALE_CLUBS",
|
||||||
|
"WHOLESALE_FLORIST_SUPPLIERS",
|
||||||
|
"WHOLESALE_PRESCRIPTION_DRUGS",
|
||||||
|
"WILDLIFE_PRODUCTS",
|
||||||
|
"WIRE_TRANSFER",
|
||||||
|
"WIRE_TRANSFER_AND_MONEY_ORDER",
|
||||||
|
"WOMEN_ACCESSORY_SPECIALITY",
|
||||||
|
"WOMEN_CLOTHING",
|
||||||
|
];
|
||||||
|
|
||||||
|
let Fs = require("fs");
|
||||||
|
let categoriesMap = categories.reduce(function (obj, name) {
|
||||||
|
obj[name] = name;
|
||||||
|
return obj;
|
||||||
|
}, {});
|
||||||
|
Fs.mkdirSync(__dirname + "/../lib", { recursive: true });
|
||||||
|
Fs.writeFileSync(
|
||||||
|
__dirname + "/../lib/categories.json",
|
||||||
|
JSON.stringify(categoriesMap, null, 2),
|
||||||
|
"utf8"
|
||||||
|
);
|
Loading…
x
Reference in New Issue
Block a user