Replaces MongoDB support with PostgreSQL (#151)
- All schemas have been rewritten to a relational model - All services have been rewritten to accommodate the new data model - Adds idempotency keys to core endpoints allowing you to retry requests with no additional side effects - Adds staged jobs to avoid putting jobs in the queue when transactions abort - Adds atomic transactions to all methods with access to the data layer Co-authored-by: Oliver Windall Juhl <oliver@mrbltech.com>
This commit is contained in:
1 parent
5f819486fc
commit
f1baca3cbd
499 files changed
+25908
-16127
No files matched your search
@@ -7,12 +7,12 @@ routes:
|
|||||||
path: /orders/:id
|
path: /orders/:id
|
||||||
route: /orders
|
route: /orders
|
||||||
description: >
|
description: >
|
||||||
An order represents a selection of items purchased and holds information about
|
An Order represents a selection of items purchased and holds information about
|
||||||
how the items have been purchased and will be fulfilled.
|
how the items have been purchased and will be fulfilled.
|
||||||
endpoints:
|
endpoints:
|
||||||
- path: /:id
|
- path: /:id
|
||||||
method: GET
|
method: GET
|
||||||
title: Retrieve an order
|
title: Retrieve an Order
|
||||||
params:
|
params:
|
||||||
- name: id
|
- name: id
|
||||||
type: String
|
type: String
|
||||||
@@ -34,104 +34,104 @@ response: |
|
|||||||
{
|
{
|
||||||
"order": {
|
"order": {
|
||||||
"id": "order_fi13oadpo2r3vc2g4592",
|
"id": "order_fi13oadpo2r3vc2g4592",
|
||||||
"status": "pending",
|
"status": "pending",
|
||||||
"fulfillment_status": "shipped",
|
"fulfillment_status": "shipped",
|
||||||
"payment_status": "captured",
|
"payment_status": "captured",
|
||||||
"email": "iron@man.com",
|
"email": "iron@man.com",
|
||||||
"billing_address": {
|
"billing_address": {
|
||||||
"id": "addr_WgNn0BSfIu",
|
"id": "addr_WgNn0BSfIu",
|
||||||
"customer_id": "cus_4eThzYSuGv",
|
"customer_id": "cus_4eThzYSuGv",
|
||||||
"company": "Stark Industries",
|
"company": "Stark Industries",
|
||||||
"first_name": "Tony",
|
"first_name": "Tony",
|
||||||
"last_name": "Stark",
|
"last_name": "Stark",
|
||||||
"address_1": "Hollywood Boulevard 1",
|
"address_1": "Hollywood Boulevard 1",
|
||||||
"address_2": null,
|
"address_2": null,
|
||||||
"city": "Los Angeles",
|
"city": "Los Angeles",
|
||||||
"country_code": "US",
|
"country_code": "US",
|
||||||
"province": "CA",
|
"province": "CA",
|
||||||
"postal_code": "90046",
|
"postal_code": "90046",
|
||||||
"phone": null,
|
"phone": null,
|
||||||
"created_at": "2020-12-11T17:03:54.458Z",
|
"created_at": "2020-12-11T17:03:54.458Z",
|
||||||
"updated_at": "2020-12-11T17:03:54.458Z",
|
"updated_at": "2020-12-11T17:03:54.458Z",
|
||||||
"deleted_at": null,
|
"deleted_at": null,
|
||||||
"metadata": null
|
"metadata": null
|
||||||
},
|
},
|
||||||
"shipping_address": {
|
"shipping_address": {
|
||||||
"id": "addr_WgNn0BSfIu",
|
"id": "addr_WgNn0BSfIu",
|
||||||
"customer_id": "cus_4eThzYSuGv",
|
"customer_id": "cus_4eThzYSuGv",
|
||||||
"company": "Stark Industries",
|
"company": "Stark Industries",
|
||||||
"first_name": "Tony",
|
"first_name": "Tony",
|
||||||
"last_name": "Stark",
|
"last_name": "Stark",
|
||||||
"address_1": "Hollywood Boulevard 1",
|
"address_1": "Hollywood Boulevard 1",
|
||||||
"address_2": null,
|
"address_2": null,
|
||||||
"city": "Los Angeles",
|
"city": "Los Angeles",
|
||||||
"country_code": "US",
|
"country_code": "US",
|
||||||
"province": "CA",
|
"province": "CA",
|
||||||
"postal_code": "90046",
|
"postal_code": "90046",
|
||||||
"phone": null,
|
"phone": null,
|
||||||
"created_at": "2020-12-11T17:03:54.458Z",
|
"created_at": "2020-12-11T17:03:54.458Z",
|
||||||
"updated_at": "2020-12-11T17:03:54.458Z",
|
"updated_at": "2020-12-11T17:03:54.458Z",
|
||||||
"deleted_at": null,
|
"deleted_at": null,
|
||||||
"metadata": null
|
"metadata": null
|
||||||
},
|
},
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"id": "item_fn2uaQH95vG9ZMnhj2aU03xg",
|
"id": "item_fn2uaQH95vG9ZMnhj2aU03xg",
|
||||||
"cart_id": null,
|
"cart_id": null,
|
||||||
"order_id": "order_s9RojwCU2AM8RztcldM2Uof7",
|
"order_id": "order_s9RojwCU2AM8RztcldM2Uof7",
|
||||||
"swap_id": null,
|
"swap_id": null,
|
||||||
"title": "Ironman suit",
|
"title": "Ironman suit",
|
||||||
"description": "Awesome Ironman suit",
|
"description": "Awesome Ironman suit",
|
||||||
"thumbnail": null,
|
"thumbnail": null,
|
||||||
"is_giftcard": false,
|
"is_giftcard": false,
|
||||||
"should_merge": false,
|
"should_merge": false,
|
||||||
"allow_discounts": true,
|
"allow_discounts": true,
|
||||||
"unit_price": 119600,
|
"unit_price": 119600,
|
||||||
"variant_id": "variant_rdEH6PykBuH57giw",
|
"variant_id": "variant_rdEH6PykBuH57giw",
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"fulfilled_quantity": 1,
|
"fulfilled_quantity": 1,
|
||||||
"returned_quantity": 0,
|
"returned_quantity": 0,
|
||||||
"shipped_quantity": 0,
|
"shipped_quantity": 0,
|
||||||
"created_at": "2020-12-11T17:03:54.458Z",
|
"created_at": "2020-12-11T17:03:54.458Z",
|
||||||
"updated_at": "2020-12-11T17:03:54.458Z",
|
"updated_at": "2020-12-11T17:03:54.458Z",
|
||||||
"metadata": null
|
"metadata": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"discounts": [
|
"discounts": [
|
||||||
{
|
{
|
||||||
"id": "disc_OpYQN4H8MOWHN2en",
|
"id": "disc_OpYQN4H8MOWHN2en",
|
||||||
"code": "CCC3C4LL88",
|
"code": "CCC3C4LL88",
|
||||||
"is_dynamic": true,
|
"is_dynamic": true,
|
||||||
"discount_rule_id": "dru_MDyr3lJLa00uxOsY",
|
"discount_rule_id": "dru_MDyr3lJLa00uxOsY",
|
||||||
"parent_discount_id": "disc_ubtdlkASI7bfUj81",
|
"parent_discount_id": "disc_ubtdlkASI7bfUj81",
|
||||||
"metadata": null
|
"metadata": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"customer_id": "cus_4eThzYSuGv",
|
"customer_id": "cus_4eThzYSuGv",
|
||||||
"shipping_methods": [
|
"shipping_methods": [
|
||||||
{
|
{
|
||||||
"id": "sm_77bEapbO8tkCqw3yo1NBuCUl",
|
"id": "sm_77bEapbO8tkCqw3yo1NBuCUl",
|
||||||
"shipping_option_id": "so_nRvwHhEdZw",
|
"shipping_option_id": "so_nRvwHhEdZw",
|
||||||
"order_id": "order_s9RojwCU2AM8RztcldM2Uof7",
|
"order_id": "order_s9RojwCU2AM8RztcldM2Uof7",
|
||||||
"cart_id": null,
|
"cart_id": null,
|
||||||
"swap_id": null,
|
"swap_id": null,
|
||||||
"return_id": null,
|
"return_id": null,
|
||||||
"price": 0,
|
"price": 0,
|
||||||
"data": {
|
"data": {
|
||||||
"id": "Parcel shop",
|
"id": "Parcel shop",
|
||||||
"city": "Los Angeles",
|
"city": "Los Angeles",
|
||||||
"postal": "90046"
|
"postal": "90046"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"metadata": null,
|
"metadata": null,
|
||||||
"display_id": 50433,
|
"display_id": 50433,
|
||||||
"currency_code": "dkk",
|
"currency_code": "dkk",
|
||||||
"region_id": "reg_HMnixPlOicAs7aBlXuchAGxd",
|
"region_id": "reg_HMnixPlOicAs7aBlXuchAGxd",
|
||||||
"shipping_total": 0,
|
"shipping_total": 0,
|
||||||
"discount_total": 0,
|
"discount_total": 0,
|
||||||
"tax_total": 3850000,
|
"tax_total": 3850000,
|
||||||
"subtotal": 154000,
|
"subtotal": 154000,
|
||||||
"total": 4004000
|
"total": 4004000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+8
-9
@@ -1,10 +1,9 @@
|
|||||||
0 silly argv { _: [ 'publish' ],
|
0 silly argv { _: [ 'bootstrap' ],
|
||||||
0 silly argv composed: 'publish',
|
0 silly argv lernaVersion: '3.22.1',
|
||||||
0 silly argv lernaVersion: '3.20.2',
|
0 silly argv '$0': '/usr/local/bin/lerna' }
|
||||||
0 silly argv '$0': '/usr/local/bin/lerna',
|
1 notice cli v3.22.1
|
||||||
0 silly argv bump: 'from-git' }
|
|
||||||
1 notice cli v3.20.2
|
|
||||||
2 verbose rootPath /Users/srindom/Developer/medusa-js
|
2 verbose rootPath /Users/srindom/Developer/medusa-js
|
||||||
3 error JSONError: Unexpected token : in JSON at position 9 while parsing near ' "name": "@medusajs/medusa"...' in packages/medusa/package.json
|
3 info versioning independent
|
||||||
3 error at module.exports (/Users/srindom/Developer/medusa-js/node_modules/parse-json/index.js:26:19)
|
4 error JSONError: Unexpected token < in JSON at position 34 while parsing near '...edusa-file-spaces",<<<<<<< HEAD "vers...' in packages/medusa-file-spaces/package.json
|
||||||
3 error at parse (/Users/srindom/Developer/medusa-js/node_modules/load-json-file/index.js:15:9)
|
4 error at module.exports (/Users/srindom/Developer/medusa-js/node_modules/parse-json/index.js:26:19)
|
||||||
|
4 error at parse (/Users/srindom/Developer/medusa-js/node_modules/load-json-file/index.js:15:9)
|
||||||
@@ -15,7 +15,10 @@ function preset(context, options = {}) {
|
|||||||
return {
|
return {
|
||||||
presets: [r(`@babel/preset-env`)],
|
presets: [r(`@babel/preset-env`)],
|
||||||
plugins: [
|
plugins: [
|
||||||
r(`@babel/plugin-proposal-class-properties`),
|
r(`babel-plugin-transform-typescript-metadata`),
|
||||||
|
r(`@babel/plugin-proposal-optional-chaining`),
|
||||||
|
[r(`@babel/plugin-proposal-decorators`), { legacy: true }],
|
||||||
|
[r(`@babel/plugin-proposal-class-properties`), { loose: true }],
|
||||||
r(`@babel/plugin-transform-classes`),
|
r(`@babel/plugin-transform-classes`),
|
||||||
r(`@babel/plugin-transform-instanceof`),
|
r(`@babel/plugin-transform-instanceof`),
|
||||||
r(`@babel/plugin-transform-runtime`),
|
r(`@babel/plugin-transform-runtime`),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "babel-preset-medusa-package",
|
"name": "babel-preset-medusa-package",
|
||||||
"version": "1.0.1",
|
"version": "1.0.2-alpha.787+0646bd3",
|
||||||
"author": "Sebastian Rindom <sebastian@mrbltech.com>",
|
"author": "Sebastian Rindom <sebastian@mrbltech.com>",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -9,11 +9,13 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/plugin-proposal-class-properties": "^7.12.1",
|
"@babel/plugin-proposal-class-properties": "^7.12.1",
|
||||||
|
"@babel/plugin-proposal-decorators": "^7.12.1",
|
||||||
"@babel/plugin-proposal-optional-chaining": "^7.12.1",
|
"@babel/plugin-proposal-optional-chaining": "^7.12.1",
|
||||||
"@babel/plugin-transform-classes": "^7.12.1",
|
"@babel/plugin-transform-classes": "^7.12.1",
|
||||||
"@babel/plugin-transform-instanceof": "^7.12.1",
|
"@babel/plugin-transform-instanceof": "^7.12.1",
|
||||||
"@babel/plugin-transform-runtime": "^7.12.1",
|
"@babel/plugin-transform-runtime": "^7.12.1",
|
||||||
"@babel/preset-env": "^7.12.7",
|
"@babel/preset-env": "^7.12.7",
|
||||||
|
"babel-plugin-transform-typescript-metadata": "^0.3.1",
|
||||||
"core-js": "^3.7.0"
|
"core-js": "^3.7.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
@@ -23,5 +25,6 @@
|
|||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.14.0"
|
"node": ">=10.14.0"
|
||||||
}
|
},
|
||||||
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@medusajs/medusa-cli",
|
"name": "@medusajs/medusa-cli",
|
||||||
"version": "1.0.11",
|
"version": "1.0.12-alpha.787+0646bd3",
|
||||||
"description": "Command Line interface for Medusa Commerce",
|
"description": "Command Line interface for Medusa Commerce",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -48,5 +48,5 @@
|
|||||||
"resolve-cwd": "^3.0.0",
|
"resolve-cwd": "^3.0.0",
|
||||||
"yargs": "^15.3.1"
|
"yargs": "^15.3.1"
|
||||||
},
|
},
|
||||||
"gitHead": "3cc7cbe5124cbcbb75f6e1435db4dcfaa2a60408"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -61,6 +61,22 @@ function buildLocalCommands(cli, isLocalProject) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cli
|
cli
|
||||||
|
.command({
|
||||||
|
command: `migrations [action]`,
|
||||||
|
desc: `Migrate the database to the most recent version.`,
|
||||||
|
builder: {
|
||||||
|
action: {
|
||||||
|
demand: true,
|
||||||
|
choices: ["run", "show"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
handler: handlerP(
|
||||||
|
getCommandHandler(`migrate`, (args, cmd) => {
|
||||||
|
process.env.NODE_ENV = process.env.NODE_ENV || `development`
|
||||||
|
return cmd(args)
|
||||||
|
})
|
||||||
|
),
|
||||||
|
})
|
||||||
.command({
|
.command({
|
||||||
command: `develop`,
|
command: `develop`,
|
||||||
desc: `Start development server. Watches file and rebuilds when something changes`,
|
desc: `Start development server. Watches file and rebuilds when something changes`,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-core-utils",
|
"name": "medusa-core-utils",
|
||||||
"version": "1.0.11",
|
"version": "1.0.12-alpha.787+0646bd3",
|
||||||
"description": "Core utils for Medusa",
|
"description": "Core utils for Medusa",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -30,8 +30,8 @@
|
|||||||
"prettier": "^1.19.1"
|
"prettier": "^1.19.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"joi": "^17.2.1",
|
"joi": "^17.3.0",
|
||||||
"joi-objectid": "^3.0.1"
|
"joi-objectid": "^3.0.1"
|
||||||
},
|
},
|
||||||
"gitHead": "3cc7cbe5124cbcbb75f6e1435db4dcfaa2a60408"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -7,13 +7,13 @@ Joi.address = () => {
|
|||||||
first_name: Joi.string().required(),
|
first_name: Joi.string().required(),
|
||||||
last_name: Joi.string().required(),
|
last_name: Joi.string().required(),
|
||||||
address_1: Joi.string().required(),
|
address_1: Joi.string().required(),
|
||||||
address_2: Joi.string().allow(""),
|
address_2: Joi.string().allow(null),
|
||||||
city: Joi.string().required(),
|
city: Joi.string().required(),
|
||||||
country_code: Joi.string().required(),
|
country_code: Joi.string().required(),
|
||||||
province: Joi.string().allow(""),
|
province: Joi.string().allow(null),
|
||||||
postal_code: Joi.string().required(),
|
postal_code: Joi.string().required(),
|
||||||
phone: Joi.string().optional(),
|
phone: Joi.string().optional(),
|
||||||
metadata: Joi.object(),
|
metadata: Joi.object().allow(null),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -941,28 +941,11 @@
|
|||||||
exec-sh "^0.3.2"
|
exec-sh "^0.3.2"
|
||||||
minimist "^1.2.0"
|
minimist "^1.2.0"
|
||||||
|
|
||||||
"@hapi/address@^4.1.0":
|
|
||||||
version "4.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-4.1.0.tgz#d60c5c0d930e77456fdcde2598e77302e2955e1d"
|
|
||||||
integrity sha512-SkszZf13HVgGmChdHo/PxchnSaCJ6cetVqLzyciudzZRT0jcOouIF/Q93mgjw8cce+D+4F4C1Z/WrfFN+O3VHQ==
|
|
||||||
dependencies:
|
|
||||||
"@hapi/hoek" "^9.0.0"
|
|
||||||
|
|
||||||
"@hapi/formula@^2.0.0":
|
|
||||||
version "2.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@hapi/formula/-/formula-2.0.0.tgz#edade0619ed58c8e4f164f233cda70211e787128"
|
|
||||||
integrity sha512-V87P8fv7PI0LH7LiVi8Lkf3x+KCO7pQozXRssAHNXXL9L1K+uyu4XypLXwxqVDKgyQai6qj3/KteNlrqDx4W5A==
|
|
||||||
|
|
||||||
"@hapi/hoek@^9.0.0":
|
"@hapi/hoek@^9.0.0":
|
||||||
version "9.1.0"
|
version "9.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.1.0.tgz#6c9eafc78c1529248f8f4d92b0799a712b6052c6"
|
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.1.0.tgz#6c9eafc78c1529248f8f4d92b0799a712b6052c6"
|
||||||
integrity sha512-i9YbZPN3QgfighY/1X1Pu118VUz2Fmmhd6b2n0/O8YVgGGfw0FbUYoA97k7FkpGJ+pLCFEDLUmAPPV4D1kpeFw==
|
integrity sha512-i9YbZPN3QgfighY/1X1Pu118VUz2Fmmhd6b2n0/O8YVgGGfw0FbUYoA97k7FkpGJ+pLCFEDLUmAPPV4D1kpeFw==
|
||||||
|
|
||||||
"@hapi/pinpoint@^2.0.0":
|
|
||||||
version "2.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@hapi/pinpoint/-/pinpoint-2.0.0.tgz#805b40d4dbec04fc116a73089494e00f073de8df"
|
|
||||||
integrity sha512-vzXR5MY7n4XeIvLpfl3HtE3coZYO4raKXW766R6DZw/6aLqR26iuZ109K7a0NtF2Db0jxqh7xz2AxkUwpUFybw==
|
|
||||||
|
|
||||||
"@hapi/topo@^5.0.0":
|
"@hapi/topo@^5.0.0":
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.0.0.tgz#c19af8577fa393a06e9c77b60995af959be721e7"
|
resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.0.0.tgz#c19af8577fa393a06e9c77b60995af959be721e7"
|
||||||
@@ -1153,6 +1136,23 @@
|
|||||||
"@types/yargs" "^15.0.0"
|
"@types/yargs" "^15.0.0"
|
||||||
chalk "^3.0.0"
|
chalk "^3.0.0"
|
||||||
|
|
||||||
|
"@sideway/address@^4.1.0":
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.0.tgz#0b301ada10ac4e0e3fa525c90615e0b61a72b78d"
|
||||||
|
integrity sha512-wAH/JYRXeIFQRsxerIuLjgUu2Xszam+O5xKeatJ4oudShOOirfmsQ1D6LL54XOU2tizpCYku+s1wmU0SYdpoSA==
|
||||||
|
dependencies:
|
||||||
|
"@hapi/hoek" "^9.0.0"
|
||||||
|
|
||||||
|
"@sideway/formula@^3.0.0":
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c"
|
||||||
|
integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==
|
||||||
|
|
||||||
|
"@sideway/pinpoint@^2.0.0":
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df"
|
||||||
|
integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==
|
||||||
|
|
||||||
"@sinonjs/commons@^1.7.0":
|
"@sinonjs/commons@^1.7.0":
|
||||||
version "1.7.2"
|
version "1.7.2"
|
||||||
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.7.2.tgz#505f55c74e0272b43f6c52d81946bed7058fc0e2"
|
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.7.2.tgz#505f55c74e0272b43f6c52d81946bed7058fc0e2"
|
||||||
@@ -3241,16 +3241,16 @@ joi-objectid@^3.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/joi-objectid/-/joi-objectid-3.0.1.tgz#63ace7860f8e1a993a28d40c40ffd8eff01a3668"
|
resolved "https://registry.yarnpkg.com/joi-objectid/-/joi-objectid-3.0.1.tgz#63ace7860f8e1a993a28d40c40ffd8eff01a3668"
|
||||||
integrity sha512-V/3hbTlGpvJ03Me6DJbdBI08hBTasFOmipsauOsxOSnsF1blxV537WTl1zPwbfcKle4AK0Ma4OPnzMH4LlvTpQ==
|
integrity sha512-V/3hbTlGpvJ03Me6DJbdBI08hBTasFOmipsauOsxOSnsF1blxV537WTl1zPwbfcKle4AK0Ma4OPnzMH4LlvTpQ==
|
||||||
|
|
||||||
joi@^17.2.1:
|
joi@^17.3.0:
|
||||||
version "17.2.1"
|
version "17.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/joi/-/joi-17.2.1.tgz#e5140fdf07e8fecf9bc977c2832d1bdb1e3f2a0a"
|
resolved "https://registry.yarnpkg.com/joi/-/joi-17.3.0.tgz#f1be4a6ce29bc1716665819ac361dfa139fff5d2"
|
||||||
integrity sha512-YT3/4Ln+5YRpacdmfEfrrKh50/kkgX3LgBltjqnlMPIYiZ4hxXZuVJcxmsvxsdeHg9soZfE3qXxHC2tMpCCBOA==
|
integrity sha512-Qh5gdU6niuYbUIUV5ejbsMiiFmBdw8Kcp8Buj2JntszCkCfxJ9Cz76OtHxOZMPXrt5810iDIXs+n1nNVoquHgg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@hapi/address" "^4.1.0"
|
|
||||||
"@hapi/formula" "^2.0.0"
|
|
||||||
"@hapi/hoek" "^9.0.0"
|
"@hapi/hoek" "^9.0.0"
|
||||||
"@hapi/pinpoint" "^2.0.0"
|
|
||||||
"@hapi/topo" "^5.0.0"
|
"@hapi/topo" "^5.0.0"
|
||||||
|
"@sideway/address" "^4.1.0"
|
||||||
|
"@sideway/formula" "^3.0.0"
|
||||||
|
"@sideway/pinpoint" "^2.0.0"
|
||||||
|
|
||||||
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-file-spaces",
|
"name": "medusa-file-spaces",
|
||||||
"version": "1.0.13",
|
"version": "1.0.12-alpha.176+0646bd3",
|
||||||
"description": "Digital Ocean Spaces file connector for Medusa",
|
"description": "Digital Ocean Spaces file connector for Medusa",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -40,9 +40,9 @@
|
|||||||
"aws-sdk": "^2.710.0",
|
"aws-sdk": "^2.710.0",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"medusa-core-utils": "^1.0.11",
|
"medusa-core-utils": "^1.0.12-alpha.787+0646bd3",
|
||||||
"medusa-test-utils": "^1.0.13",
|
"medusa-test-utils": "^1.0.12-alpha.176+0646bd3",
|
||||||
"stripe": "^8.50.0"
|
"stripe": "^8.50.0"
|
||||||
},
|
},
|
||||||
"gitHead": "3cc7cbe5124cbcbb75f6e1435db4dcfaa2a60408"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-fulfillment-manual",
|
"name": "medusa-fulfillment-manual",
|
||||||
"version": "1.0.12",
|
"version": "1.0.12-alpha.208+0646bd3",
|
||||||
"description": "A manual fulfillment provider for Medusa",
|
"description": "A manual fulfillment provider for Medusa",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
"@babel/plugin-transform-instanceof": "^7.8.3",
|
"@babel/plugin-transform-instanceof": "^7.8.3",
|
||||||
"@babel/runtime": "^7.7.6",
|
"@babel/runtime": "^7.7.6",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"medusa-core-utils": "^1.0.11"
|
"medusa-core-utils": "^1.0.12-alpha.787+0646bd3"
|
||||||
},
|
},
|
||||||
"gitHead": "3cc7cbe5124cbcbb75f6e1435db4dcfaa2a60408"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -36,6 +36,11 @@ class ManualFulfillmentService extends FulfillmentService {
|
|||||||
return Promise.resolve({})
|
return Promise.resolve({})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createFulfillment() {
|
||||||
|
// No data is being sent anywhere
|
||||||
|
return Promise.resolve({})
|
||||||
|
}
|
||||||
|
|
||||||
cancelFulfillment() {
|
cancelFulfillment() {
|
||||||
return Promise.resolve({})
|
return Promise.resolve({})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-fulfillment-webshipper",
|
"name": "medusa-fulfillment-webshipper",
|
||||||
"version": "1.0.11",
|
"version": "1.0.10-alpha.166+0646bd3",
|
||||||
"description": "Webshipper Fulfillment provider for Medusa",
|
"description": "Webshipper Fulfillment provider for Medusa",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"medusa-core-utils": "^1.0.11"
|
"medusa-core-utils": "^1.0.12-alpha.787+0646bd3"
|
||||||
},
|
},
|
||||||
"gitHead": "3cc7cbe5124cbcbb75f6e1435db4dcfaa2a60408"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -7,10 +7,18 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
|||||||
constructor({ logger, swapService, orderService }, options) {
|
constructor({ logger, swapService, orderService }, options) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
this.logger_ = logger
|
|
||||||
this.orderService_ = orderService
|
|
||||||
this.options_ = options
|
this.options_ = options
|
||||||
|
|
||||||
|
/** @private @const {logger} */
|
||||||
|
this.logger_ = logger
|
||||||
|
|
||||||
|
/** @private @const {OrderService} */
|
||||||
|
this.orderService_ = orderService
|
||||||
|
|
||||||
|
/** @private @const {SwapService} */
|
||||||
this.swapService_ = swapService
|
this.swapService_ = swapService
|
||||||
|
|
||||||
|
/** @private @const {AxiosClient} */
|
||||||
this.client_ = new Webshipper({
|
this.client_ = new Webshipper({
|
||||||
account: this.options_.account,
|
account: this.options_.account,
|
||||||
token: this.options_.api_token,
|
token: this.options_.api_token,
|
||||||
@@ -38,15 +46,19 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
async validateFulfillmentData(data, _) {
|
async validateFulfillmentData(optionData, data, _) {
|
||||||
if (data.require_drop_point) {
|
if (optionData.require_drop_point) {
|
||||||
if (!data.drop_point_id) {
|
if (!data.drop_point_id) {
|
||||||
throw new Error("Must have drop point id")
|
throw new Error("Must have drop point id")
|
||||||
} else {
|
} else {
|
||||||
// TODO: validate that the drop point exists
|
// TODO: validate that the drop point exists
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return data
|
|
||||||
|
return {
|
||||||
|
...optionData,
|
||||||
|
...data,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async validateOption(data) {
|
async validateOption(data) {
|
||||||
@@ -69,7 +81,22 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
|||||||
* Creates a return shipment in webshipper using the given method data, and
|
* Creates a return shipment in webshipper using the given method data, and
|
||||||
* return lines.
|
* return lines.
|
||||||
*/
|
*/
|
||||||
async createReturn(methodData, returnLines, fromOrder) {
|
async createReturn(returnOrder) {
|
||||||
|
let fromOrder
|
||||||
|
if (returnOrder.order_id) {
|
||||||
|
fromOrder = await this.orderService_.retrieve(returnOrder.order_id, {
|
||||||
|
select: ["total"],
|
||||||
|
relations: ["shipping_address", "returns"],
|
||||||
|
})
|
||||||
|
} else if (returnOrder.swap) {
|
||||||
|
fromOrder = await this.orderService_.retrieve(returnOrder.swap.order_id, {
|
||||||
|
select: ["total"],
|
||||||
|
relations: ["shipping_address", "returns"],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const methodData = returnOrder.shipping_method.data
|
||||||
|
|
||||||
const relationships = {
|
const relationships = {
|
||||||
shipping_rate: {
|
shipping_rate: {
|
||||||
data: {
|
data: {
|
||||||
@@ -79,7 +106,8 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const existing = fromOrder.metadata.webshipper_order_id
|
const existing =
|
||||||
|
fromOrder.metadata && fromOrder.metadata.webshipper_order_id
|
||||||
if (existing) {
|
if (existing) {
|
||||||
relationships.order = {
|
relationships.order = {
|
||||||
data: {
|
data: {
|
||||||
@@ -93,8 +121,9 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
|||||||
if (this.invoiceGenerator_) {
|
if (this.invoiceGenerator_) {
|
||||||
const base64Invoice = await this.invoiceGenerator_.createReturnInvoice(
|
const base64Invoice = await this.invoiceGenerator_.createReturnInvoice(
|
||||||
fromOrder,
|
fromOrder,
|
||||||
returnLines
|
returnOrder.items
|
||||||
)
|
)
|
||||||
|
|
||||||
docs.push({
|
docs.push({
|
||||||
document_size: "A4",
|
document_size: "A4",
|
||||||
document_format: "PDF",
|
document_format: "PDF",
|
||||||
@@ -108,7 +137,7 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
|||||||
type: "shipments",
|
type: "shipments",
|
||||||
attributes: {
|
attributes: {
|
||||||
reference: `R${fromOrder.display_id}-${fromOrder.returns.length + 1}`,
|
reference: `R${fromOrder.display_id}-${fromOrder.returns.length + 1}`,
|
||||||
ext_ref: `${fromOrder._id}.${fromOrder.returns.length}`,
|
ext_ref: `${fromOrder.id}.${returnOrder.id}`,
|
||||||
is_return: true,
|
is_return: true,
|
||||||
included_documents: docs,
|
included_documents: docs,
|
||||||
packages: [
|
packages: [
|
||||||
@@ -121,21 +150,20 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
|||||||
width: 15,
|
width: 15,
|
||||||
length: 15,
|
length: 15,
|
||||||
},
|
},
|
||||||
customs_lines: returnLines.map((item) => {
|
customs_lines: returnOrder.items.map(({ item, quantity }) => {
|
||||||
return {
|
return {
|
||||||
ext_ref: item._id,
|
ext_ref: item.id,
|
||||||
sku: item.content.variant.sku,
|
sku: item.variant.sku,
|
||||||
description: item.title,
|
description: item.title,
|
||||||
quantity: item.quantity,
|
quantity: quantity,
|
||||||
country_of_origin:
|
country_of_origin:
|
||||||
item.content.variant.metadata &&
|
item.variant.origin_country ||
|
||||||
item.content.variant.metadata.origin_country,
|
item.variant.product.origin_country,
|
||||||
tarif_number:
|
tarif_number:
|
||||||
item.content.variant.metadata &&
|
item.variant.hs_code || item.variant.product.hs_code,
|
||||||
item.content.variant.metadata.hs_code,
|
unit_price: item.unit_price / 100,
|
||||||
unit_price: item.content.unit_price,
|
vat_percent: fromOrder.tax_rate,
|
||||||
vat_percent: fromOrder.tax_rate * 100,
|
currency: fromOrder.currency_code.toUpperCase(),
|
||||||
currency: fromOrder.currency_code,
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -148,7 +176,7 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
|||||||
address_2: shipping_address.address_2,
|
address_2: shipping_address.address_2,
|
||||||
zip: shipping_address.postal_code,
|
zip: shipping_address.postal_code,
|
||||||
city: shipping_address.city,
|
city: shipping_address.city,
|
||||||
country_code: shipping_address.country_code,
|
country_code: shipping_address.country_code.toUpperCase(),
|
||||||
state: shipping_address.province,
|
state: shipping_address.province,
|
||||||
phone: shipping_address.phone,
|
phone: shipping_address.phone,
|
||||||
email: fromOrder.email,
|
email: fromOrder.email,
|
||||||
@@ -196,7 +224,12 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
|||||||
return toReturn
|
return toReturn
|
||||||
}
|
}
|
||||||
|
|
||||||
async createOrder(methodData, fulfillmentItems, fromOrder) {
|
async createFulfillment(
|
||||||
|
methodData,
|
||||||
|
fulfillmentItems,
|
||||||
|
fromOrder,
|
||||||
|
fulfillment
|
||||||
|
) {
|
||||||
const existing =
|
const existing =
|
||||||
fromOrder.metadata && fromOrder.metadata.webshipper_order_id
|
fromOrder.metadata && fromOrder.metadata.webshipper_order_id
|
||||||
|
|
||||||
@@ -228,13 +261,12 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let visible_ref = `${fromOrder.display_id}-${
|
let id = fulfillment.id
|
||||||
fromOrder.fulfillments.length + 1
|
let visible_ref = `${fromOrder.display_id}-${id.substr(id.length - 4)}`
|
||||||
}`
|
let ext_ref = `${fromOrder.id}.${fulfillment.id}`
|
||||||
let ext_ref = `${fromOrder._id}.${fromOrder.fulfillments.length}`
|
|
||||||
|
|
||||||
if (fromOrder.is_swap) {
|
if (fromOrder.is_swap) {
|
||||||
ext_ref = `S${fromOrder._id}.${fromOrder.fulfillments.length}`
|
ext_ref = `${fromOrder.id}.${fulfillment.id}`
|
||||||
visible_ref = `S-${fromOrder.display_id}`
|
visible_ref = `S-${fromOrder.display_id}`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,17 +279,17 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
|||||||
visible_ref,
|
visible_ref,
|
||||||
order_lines: fulfillmentItems.map((item) => {
|
order_lines: fulfillmentItems.map((item) => {
|
||||||
return {
|
return {
|
||||||
ext_ref: item._id,
|
ext_ref: item.id,
|
||||||
sku: item.content.variant.sku,
|
sku: item.variant.sku,
|
||||||
description: item.title,
|
description: item.title,
|
||||||
quantity: item.quantity,
|
quantity: item.quantity,
|
||||||
country_of_origin:
|
country_of_origin:
|
||||||
item.content.variant.metadata &&
|
item.variant.origin_country ||
|
||||||
item.content.variant.metadata.origin_country,
|
item.variant.product.origin_country,
|
||||||
tarif_number:
|
tarif_number:
|
||||||
item.content.variant.metadata &&
|
item.variant.hs_code || item.variant.product.hs_code,
|
||||||
item.content.variant.metadata.hs_code,
|
unit_price: item.unit_price / 100,
|
||||||
unit_price: item.content.unit_price,
|
vat_percent: fromOrder.tax_rate,
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
delivery_address: {
|
delivery_address: {
|
||||||
@@ -266,12 +298,12 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
|||||||
address_2: shipping_address.address_2,
|
address_2: shipping_address.address_2,
|
||||||
zip: shipping_address.postal_code,
|
zip: shipping_address.postal_code,
|
||||||
city: shipping_address.city,
|
city: shipping_address.city,
|
||||||
country_code: shipping_address.country_code,
|
country_code: shipping_address.country_code.toUpperCase(),
|
||||||
state: shipping_address.province,
|
state: shipping_address.province,
|
||||||
phone: shipping_address.phone,
|
phone: shipping_address.phone,
|
||||||
email: fromOrder.email,
|
email: fromOrder.email,
|
||||||
},
|
},
|
||||||
currency: fromOrder.currency_code,
|
currency: fromOrder.currency_code.toUpperCase(),
|
||||||
},
|
},
|
||||||
relationships: {
|
relationships: {
|
||||||
order_channel: {
|
order_channel: {
|
||||||
@@ -296,7 +328,7 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
|||||||
zip: methodData.drop_point_zip,
|
zip: methodData.drop_point_zip,
|
||||||
address_1: methodData.drop_point_address_1,
|
address_1: methodData.drop_point_address_1,
|
||||||
city: methodData.drop_point_city,
|
city: methodData.drop_point_city,
|
||||||
country_code: methodData.drop_point_country_code,
|
country_code: methodData.drop_point_country_code.toUpperCase(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (invoice) {
|
if (invoice) {
|
||||||
@@ -326,7 +358,7 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
|||||||
const wsOrder = await this.retrieveRelationship(
|
const wsOrder = await this.retrieveRelationship(
|
||||||
body.data.relationships.order
|
body.data.relationships.order
|
||||||
)
|
)
|
||||||
if (wsOrder.data.attributes.ext_ref) {
|
if (wsOrder.data && wsOrder.data.attributes.ext_ref) {
|
||||||
const trackingNumbers = body.data.attributes.tracking_links.map(
|
const trackingNumbers = body.data.attributes.tracking_links.map(
|
||||||
(l) => l.number
|
(l) => l.number
|
||||||
)
|
)
|
||||||
@@ -334,23 +366,44 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
|||||||
"."
|
"."
|
||||||
)
|
)
|
||||||
|
|
||||||
if (orderId.charAt(0) === "S") {
|
if (orderId.charAt(0).toLowerCase() === "s") {
|
||||||
const swap = await this.swapService_.retrieve(orderId.substring(1))
|
if (fulfillmentIndex.startsWith("ful")) {
|
||||||
const fulfillment = swap.fulfillments[fulfillmentIndex]
|
return this.swapService_.createShipment(
|
||||||
await this.swapService_.createShipment(
|
orderId,
|
||||||
swap._id,
|
fulfillmentIndex,
|
||||||
fulfillment._id,
|
|
||||||
trackingNumbers
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
const order = await this.orderService_.retrieve(orderId)
|
|
||||||
const fulfillment = order.fulfillments[fulfillmentIndex]
|
|
||||||
if (fulfillment) {
|
|
||||||
await this.orderService_.createShipment(
|
|
||||||
order._id,
|
|
||||||
fulfillment._id,
|
|
||||||
trackingNumbers
|
trackingNumbers
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
const swap = await this.swapService_.retrieve(orderId.substring(1), {
|
||||||
|
relations: ["fulfillments"],
|
||||||
|
})
|
||||||
|
const fulfillment = swap.fulfillments[fulfillmentIndex]
|
||||||
|
return this.swapService_.createShipment(
|
||||||
|
swap.id,
|
||||||
|
fulfillment.id,
|
||||||
|
trackingNumbers
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (fulfillmentIndex.startsWith("ful")) {
|
||||||
|
return this.orderService_.createShipment(
|
||||||
|
orderId,
|
||||||
|
fulfillmentIndex,
|
||||||
|
trackingNumbers
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
const order = await this.orderService_.retrieve(orderId, {
|
||||||
|
relations: ["fulfillments"],
|
||||||
|
})
|
||||||
|
|
||||||
|
const fulfillment = order.fulfillments[fulfillmentIndex]
|
||||||
|
if (fulfillment) {
|
||||||
|
return this.orderService_.createShipment(
|
||||||
|
order.id,
|
||||||
|
fulfillment.id,
|
||||||
|
trackingNumbers
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -392,7 +445,7 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
|||||||
shipping_rate_id: id,
|
shipping_rate_id: id,
|
||||||
delivery_address: {
|
delivery_address: {
|
||||||
zip,
|
zip,
|
||||||
country_code: countryCode,
|
country_code: countryCode.toUpperCase(),
|
||||||
address_1: address1,
|
address_1: address1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-interfaces",
|
"name": "medusa-interfaces",
|
||||||
"version": "1.0.14",
|
"version": "1.0.14-alpha.170+0646bd3",
|
||||||
"description": "Core interfaces for Medusa",
|
"description": "Core interfaces for Medusa",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -28,13 +28,14 @@
|
|||||||
"cross-env": "^5.2.1",
|
"cross-env": "^5.2.1",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"jest": "^25.5.2",
|
"jest": "^25.5.2",
|
||||||
"prettier": "^1.19.1"
|
"prettier": "^1.19.1",
|
||||||
|
"typeorm": "^0.2.29"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"mongoose": "5.x"
|
"typeorm": "0.x"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"medusa-core-utils": "^1.0.11"
|
"medusa-core-utils": "^1.0.12-alpha.787+0646bd3"
|
||||||
},
|
},
|
||||||
"gitHead": "3cc7cbe5124cbcbb75f6e1435db4dcfaa2a60408"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,39 @@
|
|||||||
import BaseService from "../base-service"
|
import BaseService from "../base-service"
|
||||||
|
import { In, Not } from "typeorm"
|
||||||
|
|
||||||
describe("BaseService", () => {
|
describe("BaseService", () => {
|
||||||
|
describe("buildQuery_", () => {
|
||||||
|
const baseService = new BaseService()
|
||||||
|
|
||||||
|
it("successfully creates query", () => {
|
||||||
|
const q = baseService.buildQuery_(
|
||||||
|
{
|
||||||
|
id: "1234",
|
||||||
|
test1: ["123", "12", "1"],
|
||||||
|
test2: Not("this"),
|
||||||
|
rec: {
|
||||||
|
first: ["1", "2", "3"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relations: ["1234"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(q).toEqual({
|
||||||
|
where: {
|
||||||
|
id: "1234",
|
||||||
|
test1: In(["123", "12", "1"]),
|
||||||
|
test2: Not("this"),
|
||||||
|
rec: {
|
||||||
|
first: In(["1", "2", "3"]),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
relations: ["1234"],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("addDecorator", () => {
|
describe("addDecorator", () => {
|
||||||
const baseService = new BaseService()
|
const baseService = new BaseService()
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import { MedusaError } from "medusa-core-utils"
|
||||||
|
import { In, FindOperator, getManager } from "typeorm"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common functionality for Services
|
* Common functionality for Services
|
||||||
* @interface
|
* @interface
|
||||||
@@ -7,6 +10,179 @@ class BaseService {
|
|||||||
this.decorators_ = []
|
this.decorators_ = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
withTransaction() {
|
||||||
|
console.log("WARN: withTransaction called without custom implementation")
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to build TypeORM queries.
|
||||||
|
*/
|
||||||
|
buildQuery_(selector, config = {}) {
|
||||||
|
const build = obj => {
|
||||||
|
const where = Object.entries(obj).reduce((acc, [key, value]) => {
|
||||||
|
switch (true) {
|
||||||
|
case value instanceof FindOperator:
|
||||||
|
acc[key] = value
|
||||||
|
break
|
||||||
|
case Array.isArray(value):
|
||||||
|
acc[key] = In([...value])
|
||||||
|
break
|
||||||
|
case value !== null && typeof value === "object":
|
||||||
|
acc[key] = build(value)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
acc[key] = value
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
return where
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = {
|
||||||
|
where: build(selector),
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("skip" in config) {
|
||||||
|
query.skip = config.skip
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("take" in config) {
|
||||||
|
query.take = config.take
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("relations" in config) {
|
||||||
|
query.relations = config.relations
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("select" in config) {
|
||||||
|
query.select = config.select
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("order" in config) {
|
||||||
|
query.order = config.order
|
||||||
|
}
|
||||||
|
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Confirms whether a given raw id is valid. Fails if the provided
|
||||||
|
* id is null or undefined. The validate function takes an optional config
|
||||||
|
* param, to support checking id prefix and length.
|
||||||
|
* @param {string} rawId - the id to validate.
|
||||||
|
* @param {object?} config - optional config
|
||||||
|
* @returns {string} the rawId given that nothing failed
|
||||||
|
*/
|
||||||
|
validateId_(rawId, config = {}) {
|
||||||
|
const { prefix, length } = config
|
||||||
|
if (!rawId) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Failed to validate id: ${rawId}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prefix || length) {
|
||||||
|
const [pre, rand] = rawId.split("_")
|
||||||
|
if (prefix && pre !== prefix) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`The provided id: ${rawId} does not adhere to prefix constraint: ${prefix}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length && length !== rand.length) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`The provided id: ${rawId} does not adhere to length constraint: ${length}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rawId
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldRetryTransaction(err) {
|
||||||
|
const code = typeof err === "object" ? String(err.code) : null
|
||||||
|
return code === "40001" || code === "40P01"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps some work within a transactional block. If the service already has
|
||||||
|
* a transaction manager attached this will be reused, otherwise a new
|
||||||
|
* transaction manager is created.
|
||||||
|
* @param {function} work - the transactional work to be done
|
||||||
|
* @param {string} isolation - the isolation level to be used for the work.
|
||||||
|
* @return {any} the result of the transactional work
|
||||||
|
*/
|
||||||
|
async atomicPhase_(work, isolation) {
|
||||||
|
if (this.transactionManager_) {
|
||||||
|
return work(this.transactionManager_)
|
||||||
|
} else {
|
||||||
|
const temp = this.manager_
|
||||||
|
const doWork = async m => {
|
||||||
|
this.manager_ = m
|
||||||
|
this.transactionManager_ = m
|
||||||
|
try {
|
||||||
|
const result = await work(m)
|
||||||
|
this.manager_ = temp
|
||||||
|
this.transactionManager_ = undefined
|
||||||
|
return result
|
||||||
|
} catch (error) {
|
||||||
|
this.manager_ = temp
|
||||||
|
this.transactionManager_ = undefined
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isolation) {
|
||||||
|
let result
|
||||||
|
try {
|
||||||
|
result = await this.manager_.transaction(isolation, m => doWork(m))
|
||||||
|
return result
|
||||||
|
} catch (error) {
|
||||||
|
if (this.shouldRetryTransaction(error)) {
|
||||||
|
return this.manager_.transaction(isolation, m => doWork(m))
|
||||||
|
} else {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.manager_.transaction(m => doWork(m))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dedicated method to set metadata.
|
||||||
|
* @param {string} obj - the entity to apply metadata to.
|
||||||
|
* @param {object} metadata - the metadata to set
|
||||||
|
* @return {Promise} resolves to the updated result.
|
||||||
|
*/
|
||||||
|
setMetadata_(obj, metadata) {
|
||||||
|
const existing = obj.metadata || {}
|
||||||
|
const newData = {}
|
||||||
|
for (const [key, value] of Object.entries(metadata)) {
|
||||||
|
if (typeof key !== "string") {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_ARGUMENT,
|
||||||
|
"Key type is invalid. Metadata keys must be strings"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
newData[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
const updated = {
|
||||||
|
...existing,
|
||||||
|
...newData,
|
||||||
|
}
|
||||||
|
|
||||||
|
return updated
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a decorator to a service. The decorator must be a function and should
|
* Adds a decorator to a service. The decorator must be a function and should
|
||||||
* return a decorated object.
|
* return a decorated object.
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class BaseFulfillmentService extends BaseService {
|
|||||||
throw Error("calculatePrice must be overridden by the child class")
|
throw Error("calculatePrice must be overridden by the child class")
|
||||||
}
|
}
|
||||||
|
|
||||||
createOrder() {
|
createFulfillment() {
|
||||||
throw Error("createOrder must be overridden by the child class")
|
throw Error("createOrder must be overridden by the child class")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-payment-adyen",
|
"name": "medusa-payment-adyen",
|
||||||
"version": "1.0.13",
|
"version": "1.0.12-alpha.176+0646bd3",
|
||||||
"description": "Adyen Payment provider for Medusa Commerce",
|
"description": "Adyen Payment provider for Medusa Commerce",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
"cross-env": "^7.0.2",
|
"cross-env": "^7.0.2",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"jest": "^25.5.2",
|
"jest": "^25.5.2",
|
||||||
"medusa-test-utils": "^1.0.13"
|
"medusa-test-utils": "^1.0.12-alpha.176+0646bd3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "babel src -d .",
|
"build": "babel src -d .",
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"medusa-core-utils": "^1.0.11"
|
"medusa-core-utils": "^1.0.12-alpha.787+0646bd3"
|
||||||
},
|
},
|
||||||
"gitHead": "3cc7cbe5124cbcbb75f6e1435db4dcfaa2a60408"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
export default async (req, res) => {
|
||||||
|
const adyenService = req.scope.resolve("adyenService")
|
||||||
|
const eventBus = req.scope.resolve("eventBusService")
|
||||||
|
|
||||||
|
const notification = req.body
|
||||||
|
const event = notification.notificationItems[0].NotificationRequestItem
|
||||||
|
|
||||||
|
const valid = adyenService.validateNotification(event)
|
||||||
|
|
||||||
|
if (!valid) {
|
||||||
|
res.status(401).send(`Unauthorized webhook event`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
eventBus.emit("adyen.notification_received", event)
|
||||||
|
res.status(200).send("[accepted]")
|
||||||
|
}
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
export default async (req, res) => {
|
|
||||||
const adyenService = req.scope.resolve("adyenService")
|
|
||||||
|
|
||||||
const notification = req.body
|
|
||||||
const event = notification.notificationItems[0].NotificationRequestItem
|
|
||||||
|
|
||||||
const valid = adyenService.validateNotification(event)
|
|
||||||
|
|
||||||
if (!valid) {
|
|
||||||
res.status(401).send(`Unauthorized webhook event`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.success === "true" && event.eventCode === "AUTHORISATION") {
|
|
||||||
const orderService = req.scope.resolve("orderService")
|
|
||||||
const cartService = req.scope.resolve("cartService")
|
|
||||||
|
|
||||||
const cartId = event.additionalData["metadata.cart_id"]
|
|
||||||
|
|
||||||
try {
|
|
||||||
const order = await orderService.retrieveByCartId(cartId)
|
|
||||||
console.log(order)
|
|
||||||
} catch (error) {
|
|
||||||
const cart = await cartService.retrieve(cartId)
|
|
||||||
await orderService.createFromCart(cart)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create from cart
|
|
||||||
res.status(200).send("[accepted]")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.success === "true" && event.eventCode === "CAPTURE") {
|
|
||||||
// Create from cart
|
|
||||||
console.log("Captured")
|
|
||||||
res.status(200).send("[accepted]")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
res.status(200).send("[accepted]")
|
|
||||||
}
|
|
||||||
@@ -18,12 +18,12 @@ export default (app, rootDirectory) => {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
app.use("/adyen-hooks", route)
|
app.use("/adyen/webhooks", route)
|
||||||
|
|
||||||
route.post(
|
route.post(
|
||||||
"/capture",
|
"/notification",
|
||||||
bodyParser.json(),
|
bodyParser.json(),
|
||||||
middlewares.wrap(require("./capture-hook").default)
|
middlewares.wrap(require("./adyen-notification").default)
|
||||||
)
|
)
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
import { Validator, MedusaError } from "medusa-core-utils"
|
|
||||||
|
|
||||||
export default async (req, res) => {
|
|
||||||
const schema = Validator.object().keys({
|
|
||||||
cart_id: Validator.string().required(),
|
|
||||||
provider_id: Validator.string().required(),
|
|
||||||
payment_data: Validator.object().required(),
|
|
||||||
})
|
|
||||||
|
|
||||||
const { value, error } = schema.validate(req.body)
|
|
||||||
if (error) {
|
|
||||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const cartService = req.scope.resolve("cartService")
|
|
||||||
const paymentProvider = req.scope.resolve(`pp_${value.provider_id}`)
|
|
||||||
|
|
||||||
const cart = await cartService.retrieve(value.cart_id)
|
|
||||||
|
|
||||||
const { data } = await paymentProvider.authorizePayment(
|
|
||||||
cart,
|
|
||||||
value.payment_data.paymentMethod
|
|
||||||
)
|
|
||||||
|
|
||||||
const transactionReference = data.pspReference
|
|
||||||
|
|
||||||
let newPaymentSession = cart.payment_sessions.find(
|
|
||||||
(ps) => ps.provider_id === value.provider_id
|
|
||||||
)
|
|
||||||
|
|
||||||
newPaymentSession = {
|
|
||||||
...newPaymentSession,
|
|
||||||
data,
|
|
||||||
}
|
|
||||||
|
|
||||||
await cartService.setMetadata(
|
|
||||||
cart._id,
|
|
||||||
"adyen_transaction_ref",
|
|
||||||
transactionReference
|
|
||||||
)
|
|
||||||
|
|
||||||
await cartService.updatePaymentSession(
|
|
||||||
cart._id,
|
|
||||||
value.provider_id,
|
|
||||||
newPaymentSession
|
|
||||||
)
|
|
||||||
|
|
||||||
await cartService.setPaymentMethod(cart._id, {
|
|
||||||
provider_id: value.provider_id,
|
|
||||||
data,
|
|
||||||
})
|
|
||||||
|
|
||||||
res.status(200).json({ data })
|
|
||||||
} catch (err) {
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import { Validator, MedusaError } from "medusa-core-utils"
|
|
||||||
|
|
||||||
export default async (req, res) => {
|
|
||||||
const schema = Validator.object().keys({
|
|
||||||
payload: Validator.string().required(),
|
|
||||||
payment_data: Validator.string().required(),
|
|
||||||
provider_id: Validator.string().required(),
|
|
||||||
})
|
|
||||||
|
|
||||||
const { value, error } = schema.validate(req.body)
|
|
||||||
if (error) {
|
|
||||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const adyen = req.scope.resolve("adyenService")
|
|
||||||
const paymentProvider = req.scope.resolve(`pp_${value.provider_id}`)
|
|
||||||
|
|
||||||
const { data } = await adyen.checkPaymentResult(
|
|
||||||
value.payment_data,
|
|
||||||
value.payload
|
|
||||||
)
|
|
||||||
|
|
||||||
const status = await paymentProvider.getStatus(data)
|
|
||||||
|
|
||||||
res.status(200).json({ status })
|
|
||||||
} catch (err) {
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -26,29 +26,5 @@ export default (app, rootDirectory) => {
|
|||||||
middlewares.wrap(require("./retrieve-payment-methods").default)
|
middlewares.wrap(require("./retrieve-payment-methods").default)
|
||||||
)
|
)
|
||||||
|
|
||||||
route.post(
|
|
||||||
"/authorize",
|
|
||||||
bodyParser.json(),
|
|
||||||
middlewares.wrap(require("./authorize-payment").default)
|
|
||||||
)
|
|
||||||
|
|
||||||
route.post(
|
|
||||||
"/update",
|
|
||||||
bodyParser.json(),
|
|
||||||
middlewares.wrap(require("./update-payment").default)
|
|
||||||
)
|
|
||||||
|
|
||||||
route.post(
|
|
||||||
"/payment-status",
|
|
||||||
bodyParser.json(),
|
|
||||||
middlewares.wrap(require("./check-payment-status").default)
|
|
||||||
)
|
|
||||||
|
|
||||||
route.get(
|
|
||||||
"/payment-status",
|
|
||||||
bodyParser.json(),
|
|
||||||
middlewares.wrap(require("./check-payment-status").default)
|
|
||||||
)
|
|
||||||
|
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
@@ -13,39 +13,44 @@ export default async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
const adyenService = req.scope.resolve("adyenService")
|
const adyenService = req.scope.resolve("adyenService")
|
||||||
const cartService = req.scope.resolve("cartService")
|
const cartService = req.scope.resolve("cartService")
|
||||||
const regionService = req.scope.resolve("regionService")
|
|
||||||
const totalsService = req.scope.resolve("totalsService")
|
|
||||||
|
|
||||||
const cart = await cartService.retrieve(value.cart_id)
|
const cart = await cartService.retrieve(value.cart_id, {
|
||||||
const region = await regionService.retrieve(cart.region_id)
|
select: ["total"],
|
||||||
const total = await totalsService.getTotal(cart)
|
relations: ["region", "region.payment_providers", "payment_sessions"],
|
||||||
|
})
|
||||||
|
|
||||||
const allowedMethods = cart.payment_sessions.map(
|
const allowedMethods = cart.payment_sessions.map((ps) => {
|
||||||
(ps) => ps.provider_id.split("Adyen")[0]
|
if (ps.provider_id.includes("adyen")) {
|
||||||
)
|
return ps.provider_id.split("-adyen")[0]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
if (allowedMethods.length === 0) {
|
if (allowedMethods.length === 0) {
|
||||||
res.status(200).json({ paymentMethods: {} })
|
res.status(200).json({ paymentMethods: {} })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data } = await adyenService.retrievePaymentMethods(
|
const pmMethods = await adyenService.retrievePaymentMethods(
|
||||||
cart,
|
|
||||||
allowedMethods,
|
allowedMethods,
|
||||||
total,
|
cart.total,
|
||||||
region.currency_code
|
cart.currency_code,
|
||||||
|
cart.customer_id || ""
|
||||||
)
|
)
|
||||||
|
|
||||||
// Adyen does not behave 100% correctly in regards to allowed methods
|
// Adyen does not behave 100% correctly in regards to allowed methods
|
||||||
// Therefore, we sanity filter before sending them to the storefront
|
// Therefore, we sanity filter before sending them to the storefront
|
||||||
const { paymentMethods, groups } = data
|
const { paymentMethods, groups, storedPaymentMethods } = pmMethods
|
||||||
const methods = paymentMethods.filter((pm) =>
|
const methods = paymentMethods.filter((pm) =>
|
||||||
allowedMethods.includes(pm.type)
|
allowedMethods.includes(pm.type)
|
||||||
)
|
)
|
||||||
|
|
||||||
res
|
const response = {
|
||||||
.status(200)
|
paymentMethods: methods,
|
||||||
.json({ paymentMethods: { paymentMethods: methods, groups } })
|
groups,
|
||||||
|
storedPaymentMethods,
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(200).json({ payment_methods: response })
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
import { Validator, MedusaError } from "medusa-core-utils"
|
|
||||||
|
|
||||||
export default async (req, res) => {
|
|
||||||
const schema = Validator.object().keys({
|
|
||||||
cart_id: Validator.string().required(),
|
|
||||||
provider_id: Validator.string().required(),
|
|
||||||
payment_data: Validator.object().required(),
|
|
||||||
})
|
|
||||||
|
|
||||||
const { value, error } = schema.validate(req.body)
|
|
||||||
if (error) {
|
|
||||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const cartService = req.scope.resolve("cartService")
|
|
||||||
const paymentProvider = req.scope.resolve(`pp_${value.provider_id}`)
|
|
||||||
|
|
||||||
const cart = await cartService.retrieve(value.cart_id)
|
|
||||||
|
|
||||||
const { data } = await paymentProvider.updatePayment(
|
|
||||||
value.payment_data.paymentData,
|
|
||||||
value.payment_data.details
|
|
||||||
)
|
|
||||||
|
|
||||||
await cartService.updatePaymentSession(cart._id, value.provider_id, data)
|
|
||||||
|
|
||||||
res.status(200).json({ data })
|
|
||||||
} catch (err) {
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,72 +2,141 @@ import axios from "axios"
|
|||||||
import _ from "lodash"
|
import _ from "lodash"
|
||||||
import { hmacValidator } from "@adyen/api-library"
|
import { hmacValidator } from "@adyen/api-library"
|
||||||
import { BaseService } from "medusa-interfaces"
|
import { BaseService } from "medusa-interfaces"
|
||||||
|
import { Client, Config, CheckoutAPI } from "@adyen/api-library"
|
||||||
|
|
||||||
class AdyenService extends BaseService {
|
class AdyenService extends BaseService {
|
||||||
constructor({ regionService, cartService, totalsService }, options) {
|
constructor({ cartService }, options) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
this.regionService_ = regionService
|
/** @private @constant {CartService} */
|
||||||
|
|
||||||
this.cartService_ = cartService
|
this.cartService_ = cartService
|
||||||
|
|
||||||
this.totalsService_ = totalsService
|
/**
|
||||||
|
* {
|
||||||
|
* api_key: "",
|
||||||
|
* notification_hmac: "",
|
||||||
|
* return_url: "",
|
||||||
|
* merchant_account: "",
|
||||||
|
* origin: "",
|
||||||
|
* environment: "",
|
||||||
|
* live_endpoint_prefix: "",
|
||||||
|
* payment_endpoint: ""
|
||||||
|
* }
|
||||||
|
*/
|
||||||
this.options_ = options
|
this.options_ = options
|
||||||
|
|
||||||
this.adyenCheckoutApi = axios.create({
|
/** @private @constant {AxiosClient} */
|
||||||
baseURL: "https://checkout-test.adyen.com/v53",
|
this.adyenClient_ = this.initAdyenClient()
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
/** @private @constant {AdyenClient} */
|
||||||
"x-API-key": this.options_.api_key,
|
this.adyenPaymentApi = this.initPaymentClient()
|
||||||
},
|
}
|
||||||
|
|
||||||
|
withTransaction(transactionManager) {
|
||||||
|
if (!transactionManager) {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
const cloned = new AdyenService({
|
||||||
|
cartService: this.cartService_,
|
||||||
|
totalsService: this.totalsService_,
|
||||||
})
|
})
|
||||||
|
|
||||||
this.adyenPaymentApi = axios.create({
|
this.transactionManager_ = transactionManager
|
||||||
baseURL: "https://pal-test.adyen.com/pal/servlet/Payment/v52",
|
|
||||||
headers: {
|
return cloned
|
||||||
"Content-Type": "application/json",
|
|
||||||
"x-API-key": this.options_.api_key,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getOptions() {
|
getOptions() {
|
||||||
return this.options_
|
return this.options_
|
||||||
}
|
}
|
||||||
|
|
||||||
validateNotification(event) {
|
initPaymentClient() {
|
||||||
|
return axios.create({
|
||||||
|
baseURL: this.options_.payment_endpoint,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"x-API-key": this.options_.api_key,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
initAdyenClient() {
|
||||||
|
const config = new Config()
|
||||||
|
config.apiKey = this.options_.api_key
|
||||||
|
config.merchantAccount = this.options_.merchant_account
|
||||||
|
|
||||||
|
const client = new Client({
|
||||||
|
config,
|
||||||
|
})
|
||||||
|
|
||||||
|
client.setEnvironment(
|
||||||
|
this.options_.environment,
|
||||||
|
this.options_.live_endpoint_prefix
|
||||||
|
)
|
||||||
|
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates an Adyen webhook notification
|
||||||
|
* @param {object} notification - notification to validate
|
||||||
|
* @returns {string} the status of the payment
|
||||||
|
*/
|
||||||
|
validateNotification(notification) {
|
||||||
const validator = new hmacValidator()
|
const validator = new hmacValidator()
|
||||||
|
|
||||||
const validated = validator.validateHMAC(
|
const validated = validator.validateHMAC(
|
||||||
event,
|
notification,
|
||||||
this.options_.notification_hmac
|
this.options_.notification_hmac
|
||||||
)
|
)
|
||||||
return validated
|
return validated
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve payment methods from Ayden using country as filter.
|
* Retrieve stored payment methods from Adyen.
|
||||||
* @param {string} countryCode - country code of cart
|
* @param {Customer} customer - customer to retrieve methods for
|
||||||
* @param {string} shopperLocale - locale used on website
|
* @returns {Promise} result containing the stored payment methods from Adyen
|
||||||
* @returns {string} the status of the payment
|
|
||||||
*/
|
*/
|
||||||
async retrievePaymentMethods(cart, allowedMethods, total, currency) {
|
async retrieveSavedMethods(customer) {
|
||||||
let request = {
|
let request = {
|
||||||
allowedPaymentMethods: allowedMethods,
|
|
||||||
amount: {
|
|
||||||
value: total * 100,
|
|
||||||
currency: currency,
|
|
||||||
},
|
|
||||||
merchantAccount: this.options_.merchant_account,
|
merchantAccount: this.options_.merchant_account,
|
||||||
channel: this.options_.channel,
|
channel: "Web",
|
||||||
}
|
shopperReference: customer.id,
|
||||||
|
|
||||||
if (cart.customer_id) {
|
|
||||||
request.shopperReference = cart.customer_id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await this.adyenCheckoutApi.post("/paymentMethods", request)
|
const checkout = new CheckoutAPI(this.adyenClient_)
|
||||||
|
const methods = await checkout.paymentMethods(request)
|
||||||
|
return methods.storedPaymentMethods || []
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve payment methods from Adyen.
|
||||||
|
* @param {string[]} allowedMethods - the allowed methods based on region
|
||||||
|
* @param {number} total - total amount to be paid with payment methods
|
||||||
|
* @param {string} currency - currency code to use for the payment
|
||||||
|
* @param {string} customerId - id of the customer paying
|
||||||
|
* @returns {Promise} result containing the payment methods from Adyen
|
||||||
|
*/
|
||||||
|
async retrievePaymentMethods(allowedMethods, total, currency, customerId) {
|
||||||
|
let request = {
|
||||||
|
allowedPaymentMethods: allowedMethods,
|
||||||
|
amount: {
|
||||||
|
value: total,
|
||||||
|
currency: currency,
|
||||||
|
},
|
||||||
|
merchantAccount: this.options_.merchant_account,
|
||||||
|
channel: "Web",
|
||||||
|
shopperReference: customerId,
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const checkout = new CheckoutAPI(this.adyenClient_)
|
||||||
|
return checkout.paymentMethods(request)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
@@ -75,89 +144,218 @@ class AdyenService extends BaseService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Status for Adyen payment.
|
* Status for Adyen payment.
|
||||||
* @param {Object} paymentData - payment method data from cart
|
* @param {object} paymentData - payment method data from cart
|
||||||
* @returns {string} the status of the payment
|
* @returns {string} the status of the payment
|
||||||
*/
|
*/
|
||||||
async getStatus(_) {
|
getStatus(paymentData) {
|
||||||
let status = "initial"
|
const { resultCode } = paymentData
|
||||||
|
let status = "pending"
|
||||||
|
|
||||||
|
if (resultCode === "Pending") {
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultCode === "Refused") {
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultCode === "Error") {
|
||||||
|
status = "error"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultCode === "Authorised") {
|
||||||
|
status = "authorized"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultCode === "Canceled") {
|
||||||
|
status = "canceled"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultCode === "ChallengeShopper") {
|
||||||
|
status = "requires_more"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultCode === "RedirectShopper") {
|
||||||
|
status = "requires_more"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultCode === "IdentifyShopper") {
|
||||||
|
status = "requires_more"
|
||||||
|
}
|
||||||
|
|
||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates Adyen payment object.
|
* Creates Adyen payment object.
|
||||||
* @param {any} _ - placeholder object
|
* @param {Cart} cart - cart to initiate payment for
|
||||||
* @returns {Object} empty payment data
|
* @returns {object} empty payment data
|
||||||
*/
|
*/
|
||||||
async createPayment(_) {
|
async createPayment(cart) {
|
||||||
return {}
|
return { cart_id: cart.id }
|
||||||
}
|
|
||||||
|
|
||||||
async retrievePayment(data) {
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
async updatePayment(paymentData, details) {
|
|
||||||
const request = {
|
|
||||||
paymentData,
|
|
||||||
details,
|
|
||||||
}
|
|
||||||
return this.adyenCheckoutApi.post("/payments/details", request)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates and authorizes an Ayden payment
|
* Retrieves Adyen payment. This is not supported by adyen, so we simply
|
||||||
* @returns {Object} payment data result
|
* return the current payment method data
|
||||||
|
* @param {object} data - payment session
|
||||||
|
* @returns {object} payment method data
|
||||||
*/
|
*/
|
||||||
async authorizePayment(cart, paymentMethod) {
|
async getPaymentData(paymentSession) {
|
||||||
const region = await this.regionService_.retrieve(cart.region_id)
|
return { ...paymentSession.data }
|
||||||
const total = await this.totalsService_.getTotal(cart)
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves Adyen payment. This is not supported by adyen, so we simply
|
||||||
|
* return the current payment method data
|
||||||
|
* @param {object} sessionData - the data of the payment to retrieve
|
||||||
|
* @returns {Promise<object>} Stripe payment intent
|
||||||
|
*/
|
||||||
|
async retrieve(sessionData) {
|
||||||
|
return sessionData
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and authorizes an Adyen payment.
|
||||||
|
* Requires cart_id in context for authorization.
|
||||||
|
* Return status of authorization result.
|
||||||
|
* @param {object} sessionData - payment session data
|
||||||
|
* @param {object} context - properties relevant to current context
|
||||||
|
* @returns {Promise<{ status: string, data: object }>} result with data and status
|
||||||
|
*/
|
||||||
|
async authorizePayment(session, context) {
|
||||||
|
const sessionData = session.data
|
||||||
|
|
||||||
|
const status = this.getStatus(sessionData)
|
||||||
|
|
||||||
|
// If session data is present, we already called authorize once.
|
||||||
|
// Therefore, this is most likely a call for getting additional details
|
||||||
|
if (status === "requires_more") {
|
||||||
|
const updated = await this.updatePaymentData(sessionData, {
|
||||||
|
details: sessionData.details,
|
||||||
|
paymentData: sessionData.paymentData,
|
||||||
|
})
|
||||||
|
|
||||||
|
return { data: updated, status: this.getStatus(updated) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === "authorized") {
|
||||||
|
return { data: sessionData, status: "authorized" }
|
||||||
|
}
|
||||||
|
|
||||||
|
const cart = await this.cartService_.retrieve(session.cart_id, {
|
||||||
|
select: ["total"],
|
||||||
|
relations: ["region", "shipping_address"],
|
||||||
|
})
|
||||||
|
|
||||||
|
const amount = {
|
||||||
|
currency: cart.region.currency_code.toUpperCase(),
|
||||||
|
value: cart.total,
|
||||||
|
}
|
||||||
|
|
||||||
let request = {
|
let request = {
|
||||||
amount: {
|
amount,
|
||||||
currency: region.currency_code,
|
shopperIP: context.ip_address || "",
|
||||||
value: total * 100,
|
|
||||||
},
|
|
||||||
shopperReference: cart.customer_id,
|
shopperReference: cart.customer_id,
|
||||||
paymentMethod,
|
paymentMethod: sessionData.paymentData.paymentMethod,
|
||||||
reference: cart._id,
|
reference: cart.id,
|
||||||
merchantAccount: this.options_.merchant_account,
|
merchantAccount: this.options_.merchant_account,
|
||||||
returnUrl: this.options_.return_url,
|
returnUrl: this.options_.return_url,
|
||||||
|
origin: this.options_.origin,
|
||||||
|
channel: "Web",
|
||||||
|
redirectFromIssuerMethod: "GET",
|
||||||
|
browserInfo: sessionData.browserInfo || {},
|
||||||
|
billingAddress: {
|
||||||
|
city: cart.shipping_address.city,
|
||||||
|
country: cart.shipping_address.country_code,
|
||||||
|
houseNumberOrName: cart.shipping_address.address_2 || "",
|
||||||
|
postalCode: cart.shipping_address.postal_code,
|
||||||
|
stateOrProvice: cart.shipping_address.province || "",
|
||||||
|
street: cart.shipping_address.address_1,
|
||||||
|
},
|
||||||
metadata: {
|
metadata: {
|
||||||
cart_id: cart._id,
|
cart_id: cart.id,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if (paymentMethod.storedPaymentMethodId) {
|
// If customer chose to save the payment method
|
||||||
|
if (sessionData.storePaymentMethod) {
|
||||||
|
request.storePaymentMethod = "true"
|
||||||
request.shopperInteraction = "Ecommerce"
|
request.shopperInteraction = "Ecommerce"
|
||||||
request.recurringProcessingModel = "CardOnFile"
|
request.recurringProcessingModel = "CardOnFile"
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.adyenCheckoutApi.post("/payments", request)
|
const checkout = new CheckoutAPI(this.adyenClient_)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const authorizedPayment = await checkout.payments(request, {
|
||||||
|
idempotencyKey: context.idempotency_key || "",
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: authorizedPayment,
|
||||||
|
status: this.getStatus(authorizedPayment),
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkPaymentResult(paymentData, payload) {
|
async updatePaymentData(sessionData, update) {
|
||||||
const request = {
|
if (_.isEmpty(update.details)) {
|
||||||
paymentData,
|
return { ...sessionData, ...update }
|
||||||
details: {
|
|
||||||
payload,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
return this.adyenCheckoutApi.post("/payments/details", request)
|
|
||||||
|
const checkout = new CheckoutAPI(this.adyenClient_)
|
||||||
|
const updated = await checkout.paymentsDetails(update)
|
||||||
|
|
||||||
|
return updated
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Captures an Ayden payment
|
* Updates an Adyen payment.
|
||||||
* @param {Object} data - payment data to capture
|
* @param {object} paymentData - payment data to update
|
||||||
* @returns {Object} payment data result of capture
|
* @param {details} details - details to update
|
||||||
|
* @returns {Promise} result of the update operation
|
||||||
*/
|
*/
|
||||||
async capturePayment(data) {
|
async updatePayment(paymentData, details) {
|
||||||
const { pspReference, amount } = data
|
return paymentData
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Additional details
|
||||||
|
* @param {object} paymentData - payment data
|
||||||
|
* @param {object} details - payment details
|
||||||
|
* @returns {Promise} current payment result
|
||||||
|
*/
|
||||||
|
async additionalDetails(paymentData, details) {
|
||||||
|
const request = {
|
||||||
|
paymentData,
|
||||||
|
details,
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkout = new CheckoutAPI(this.adyenClient_)
|
||||||
|
return checkout.paymentsDetails(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Captures an Adyen payment
|
||||||
|
* @param {object} payment - payment to capture
|
||||||
|
* @returns {string} status = processing_captures
|
||||||
|
*/
|
||||||
|
async capturePayment(payment) {
|
||||||
|
const { pspReference, merchantReference } = payment.data
|
||||||
|
const { amount, currency_code } = payment
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const captured = await this.adyenPaymentApi.post("/capture", {
|
const captured = await this.adyenPaymentApi.post("/capture", {
|
||||||
originalReference: pspReference,
|
originalReference: pspReference,
|
||||||
modificationAmount: amount,
|
modificationAmount: {
|
||||||
|
value: amount,
|
||||||
|
currency: currency_code.toUpperCase(),
|
||||||
|
},
|
||||||
merchantAccount: this.options_.merchant_account,
|
merchantAccount: this.options_.merchant_account,
|
||||||
|
reference: merchantReference,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -170,40 +368,55 @@ class AdyenService extends BaseService {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return captured
|
return { originalReference: pspReference, ...captured.data }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refunds an Ayden payment
|
* Refunds an Adyen payment
|
||||||
* @param {Object} paymentData - payment data to refund
|
* @param {object} payment - payment to refund
|
||||||
* @param {number} amountToRefund - amount to refund
|
* @param {number} amountToRefund - amount to refund
|
||||||
* @returns {Object} payment data result of refund
|
* @returns {object} payment data result of refund
|
||||||
*/
|
*/
|
||||||
async refundPayment(data) {
|
async refundPayment(payment, amountToRefund) {
|
||||||
const { pspReference, amount } = data
|
const { originalReference, merchantReference } = payment.data
|
||||||
|
const { currency_code } = payment
|
||||||
|
|
||||||
|
const refundAmount = {
|
||||||
|
currency: currency_code.toUpperCase(),
|
||||||
|
value: amountToRefund,
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return this.adyenPaymentApi.post("/refund", {
|
const refunded = await this.adyenPaymentApi.post("/refund", {
|
||||||
originalReference: pspReference,
|
originalReference,
|
||||||
merchantAccount: this.options_.merchant_account,
|
merchantAccount: this.options_.merchant_account,
|
||||||
modificationAmount: amount,
|
modificationAmount: refundAmount,
|
||||||
|
reference: merchantReference,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return { originalReference, ...refunded.data }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancels an Ayden payment
|
* Adyen does not have a way of deleting payments, hence the empty impl.
|
||||||
* @param {Object} paymentData - payment data to cancel
|
|
||||||
* @returns {Object} payment data result of cancel
|
|
||||||
*/
|
*/
|
||||||
async cancelPayment(paymentData) {
|
async deletePayment(_) {
|
||||||
const { pspReference } = paymentData
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels an Adyen payment.
|
||||||
|
* @param {object} payment - payment to cancel
|
||||||
|
* @returns {object} payment data result of cancel
|
||||||
|
*/
|
||||||
|
async cancelPayment(payment) {
|
||||||
|
const { pspReference } = payment.data
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return this.adyenPaymentApi.post("/cancel", {
|
return this.adyenPaymentApi.post("/cancel", {
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
import _ from "lodash"
|
||||||
|
import https from "https"
|
||||||
|
import fs from "fs"
|
||||||
|
import axios from "axios"
|
||||||
|
import { PaymentService } from "medusa-interfaces"
|
||||||
|
|
||||||
|
class ApplePayAdyenService extends PaymentService {
|
||||||
|
static identifier = "applepay-adyen"
|
||||||
|
|
||||||
|
constructor({ adyenService }, options) {
|
||||||
|
super()
|
||||||
|
|
||||||
|
this.adyenService_ = adyenService
|
||||||
|
|
||||||
|
this.options_ = options
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Status for Adyen payment.
|
||||||
|
* @param {Object} paymentData - payment method data from cart
|
||||||
|
* @returns {string} the status of the payment
|
||||||
|
*/
|
||||||
|
async getStatus(paymentData) {
|
||||||
|
const { resultCode } = paymentData
|
||||||
|
let status = "initial"
|
||||||
|
|
||||||
|
if (resultCode === "Authorised") {
|
||||||
|
status = "authorized"
|
||||||
|
}
|
||||||
|
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
|
async createPayment(_) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getApplePaySession(validationUrl) {
|
||||||
|
let certificate
|
||||||
|
try {
|
||||||
|
// Place certificate in root folder
|
||||||
|
certificate = fs.readFileSync("./apple-pay-cert.pem")
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(
|
||||||
|
"Could not find ApplePay certificate. Make sure to place it in root folder of your server"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const httpsAgent = new https.Agent({
|
||||||
|
cert: certificate,
|
||||||
|
key: certificate,
|
||||||
|
rejectUnauthorized: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
const request = {
|
||||||
|
merchantIdentifier: this.options_.applepay_merchant_id,
|
||||||
|
displayName: this.options_.applepay_display_name,
|
||||||
|
initiative: "web",
|
||||||
|
initiativeContext: this.options_.applepay_initiative_context,
|
||||||
|
}
|
||||||
|
|
||||||
|
return axios.post(validationUrl, request, {
|
||||||
|
httpsAgent,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async authorizePayment(sessionData, context) {
|
||||||
|
return this.adyenService_.authorizePayment(sessionData, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPaymentData(data) {
|
||||||
|
return this.adyenService_.getPaymentData(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async retrievePayment(data) {
|
||||||
|
return this.adyenService_.retrievePayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async updatePaymentData(sessionData, update) {
|
||||||
|
return this.adyenService_.updatePaymentData(sessionData, update)
|
||||||
|
}
|
||||||
|
|
||||||
|
async updatePayment(data, _) {
|
||||||
|
return this.adyenService_.updatePayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async deletePayment(data) {
|
||||||
|
return this.adyenService_.deletePayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async capturePayment(data) {
|
||||||
|
return this.adyenService_.capturePayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async refundPayment(data, amountToRefund) {
|
||||||
|
return this.adyenService_.refundPayment(data, amountToRefund)
|
||||||
|
}
|
||||||
|
|
||||||
|
async cancelPayment(data) {
|
||||||
|
return this.adyenService_.cancelPayment(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ApplePayAdyenService
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
import _ from "lodash"
|
|
||||||
import { PaymentService } from "medusa-interfaces"
|
|
||||||
|
|
||||||
class ApplePayAdyenService extends PaymentService {
|
|
||||||
static identifier = "applepayAdyen"
|
|
||||||
|
|
||||||
constructor({ adyenService }) {
|
|
||||||
super()
|
|
||||||
|
|
||||||
this.adyenService_ = adyenService
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Status for Adyen payment.
|
|
||||||
* @param {Object} paymentData - payment method data from cart
|
|
||||||
* @returns {string} the status of the payment
|
|
||||||
*/
|
|
||||||
async getStatus(paymentData) {
|
|
||||||
const { resultCode } = paymentData
|
|
||||||
let status = "initial"
|
|
||||||
|
|
||||||
if (resultCode === "Authorised") {
|
|
||||||
status = "authorized"
|
|
||||||
}
|
|
||||||
|
|
||||||
return status
|
|
||||||
}
|
|
||||||
|
|
||||||
async createPayment(_) {
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
|
|
||||||
async authorizePayment(cart, paymentMethod) {
|
|
||||||
return this.adyenService_.authorizePayment(cart, paymentMethod)
|
|
||||||
}
|
|
||||||
|
|
||||||
async retrievePayment(data) {
|
|
||||||
return this.adyenService_.retrievePayment(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
async updatePayment(data, _) {
|
|
||||||
return this.adyenService_.updatePayment(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
async deletePayment(data) {
|
|
||||||
return this.adyenService_.deletePayment(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
async capturePayment(data) {
|
|
||||||
try {
|
|
||||||
return this.adyenService_.capturePayment(data)
|
|
||||||
} catch (error) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async refundPayment(data) {
|
|
||||||
try {
|
|
||||||
return this.adyenService_.refundPayment(data)
|
|
||||||
} catch (error) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async cancelPayment(data) {
|
|
||||||
try {
|
|
||||||
return this.adyenService_.cancelPayment(data)
|
|
||||||
} catch (error) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ApplePayAdyenService
|
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import _ from "lodash"
|
||||||
|
import { PaymentService } from "medusa-interfaces"
|
||||||
|
|
||||||
|
class CardAdyenService extends PaymentService {
|
||||||
|
static identifier = "scheme-adyen"
|
||||||
|
|
||||||
|
constructor({ adyenService }) {
|
||||||
|
super()
|
||||||
|
|
||||||
|
this.adyenService_ = adyenService
|
||||||
|
}
|
||||||
|
|
||||||
|
async retrieveSavedMethods(customer) {
|
||||||
|
return this.adyenService_.retrieveSavedMethods(customer)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getStatus(paymentData) {
|
||||||
|
return this.adyenService_.getStatus(paymentData)
|
||||||
|
}
|
||||||
|
|
||||||
|
async createPayment(data) {
|
||||||
|
return this.adyenService_.createPayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async authorizePayment(sessionData, context) {
|
||||||
|
return this.adyenService_.authorizePayment(sessionData, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
async retrievePayment(data) {
|
||||||
|
return this.adyenService_.retrievePayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPaymentData(data) {
|
||||||
|
return this.adyenService_.getPaymentData(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async updatePayment(data, _) {
|
||||||
|
return this.adyenService_.updatePayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async updatePaymentData(sessionData, update) {
|
||||||
|
return this.adyenService_.updatePaymentData(sessionData, update)
|
||||||
|
}
|
||||||
|
|
||||||
|
async deletePayment(data) {
|
||||||
|
return this.adyenService_.deletePayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async capturePayment(data) {
|
||||||
|
return this.adyenService_.capturePayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async refundPayment(data, amountToRefund) {
|
||||||
|
return this.adyenService_.refundPayment(data, amountToRefund)
|
||||||
|
}
|
||||||
|
|
||||||
|
async cancelPayment(data) {
|
||||||
|
return this.adyenService_.cancelPayment(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CardAdyenService
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
import _ from "lodash"
|
|
||||||
import { PaymentService } from "medusa-interfaces"
|
|
||||||
|
|
||||||
class CardAdyenService extends PaymentService {
|
|
||||||
static identifier = "schemeAdyen"
|
|
||||||
|
|
||||||
constructor({ adyenService }) {
|
|
||||||
super()
|
|
||||||
|
|
||||||
this.adyenService_ = adyenService
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Status for Adyen payment.
|
|
||||||
* @param {Object} paymentData - payment method data from cart
|
|
||||||
* @returns {string} the status of the payment
|
|
||||||
*/
|
|
||||||
async getStatus(paymentData) {
|
|
||||||
const { resultCode } = paymentData
|
|
||||||
let status = "initial"
|
|
||||||
|
|
||||||
if (resultCode === "Authorised") {
|
|
||||||
status = "authorized"
|
|
||||||
}
|
|
||||||
|
|
||||||
return status
|
|
||||||
}
|
|
||||||
|
|
||||||
async createPayment(_) {
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
|
|
||||||
async authorizePayment(cart, paymentMethod) {
|
|
||||||
return this.adyenService_.authorizePayment(cart, paymentMethod)
|
|
||||||
}
|
|
||||||
|
|
||||||
async retrievePayment(data) {
|
|
||||||
return this.adyenService_.retrievePayment(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
async updatePayment(data, _) {
|
|
||||||
return this.adyenService_.updatePayment(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
async deletePayment(data) {
|
|
||||||
return this.adyenService_.deletePayment(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
async capturePayment(data) {
|
|
||||||
try {
|
|
||||||
return this.adyenService_.capturePayment(data)
|
|
||||||
} catch (error) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async refundPayment(data) {
|
|
||||||
try {
|
|
||||||
return this.adyenService_.refundPayment(data)
|
|
||||||
} catch (error) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async cancelPayment(data) {
|
|
||||||
try {
|
|
||||||
return this.adyenService_.cancelPayment(data)
|
|
||||||
} catch (error) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default CardAdyenService
|
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import _ from "lodash"
|
||||||
|
import { PaymentService } from "medusa-interfaces"
|
||||||
|
|
||||||
|
class GooglePayAdyenService extends PaymentService {
|
||||||
|
static identifier = "paywithgoogle-adyen"
|
||||||
|
|
||||||
|
constructor({ adyenService }) {
|
||||||
|
super()
|
||||||
|
|
||||||
|
this.adyenService_ = adyenService
|
||||||
|
}
|
||||||
|
|
||||||
|
async getStatus(paymentData) {
|
||||||
|
return this.adyenService_.getStatus(paymentData)
|
||||||
|
}
|
||||||
|
|
||||||
|
async createPayment(data) {
|
||||||
|
return this.adyenService_.createPayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async authorizePayment(sessionData, context) {
|
||||||
|
return this.adyenService_.authorizePayment(sessionData, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
async retrievePayment(data) {
|
||||||
|
return this.adyenService_.retrievePayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async updatePayment(data, _) {
|
||||||
|
return this.adyenService_.updatePayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async updatePaymentData(sessionData, update) {
|
||||||
|
return this.adyenService_.updatePaymentData(sessionData, update)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPaymentData(data) {
|
||||||
|
return this.adyenService_.getPaymentData(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async deletePayment(data) {
|
||||||
|
return this.adyenService_.deletePayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async capturePayment(data) {
|
||||||
|
return this.adyenService_.capturePayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async refundPayment(data, amountToRefund) {
|
||||||
|
return this.adyenService_.refundPayment(data, amountToRefund)
|
||||||
|
}
|
||||||
|
|
||||||
|
async cancelPayment(data) {
|
||||||
|
return this.adyenService_.cancelPayment(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GooglePayAdyenService
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
import _ from "lodash"
|
|
||||||
import { PaymentService } from "medusa-interfaces"
|
|
||||||
|
|
||||||
class GooglePayAdyenService extends PaymentService {
|
|
||||||
static identifier = "googlepayAdyen"
|
|
||||||
|
|
||||||
constructor({ adyenService }) {
|
|
||||||
super()
|
|
||||||
|
|
||||||
this.adyenService_ = adyenService
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Status for Adyen payment.
|
|
||||||
* @param {Object} paymentData - payment method data from cart
|
|
||||||
* @returns {string} the status of the payment
|
|
||||||
*/
|
|
||||||
async getStatus(paymentData) {
|
|
||||||
const { resultCode } = paymentData
|
|
||||||
let status = "initial"
|
|
||||||
|
|
||||||
if (resultCode === "Authorised") {
|
|
||||||
status = "authorized"
|
|
||||||
}
|
|
||||||
|
|
||||||
return status
|
|
||||||
}
|
|
||||||
|
|
||||||
async createPayment(_) {
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
|
|
||||||
async authorizePayment(cart, paymentMethod) {
|
|
||||||
return this.adyenService_.authorizePayment(cart, paymentMethod)
|
|
||||||
}
|
|
||||||
|
|
||||||
async retrievePayment(data) {
|
|
||||||
return this.adyenService_.retrievePayment(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
async updatePayment(data, _) {
|
|
||||||
return this.adyenService_.updatePayment(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
async deletePayment(data) {
|
|
||||||
return this.adyenService_.deletePayment(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
async capturePayment(data) {
|
|
||||||
try {
|
|
||||||
return this.adyenService_.capturePayment(data)
|
|
||||||
} catch (error) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async refundPayment(data) {
|
|
||||||
try {
|
|
||||||
return this.adyenService_.refundPayment(data)
|
|
||||||
} catch (error) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async cancelPayment(data) {
|
|
||||||
try {
|
|
||||||
return this.adyenService_.cancelPayment(data)
|
|
||||||
} catch (error) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default GooglePayAdyenService
|
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import _ from "lodash"
|
||||||
|
import { PaymentService } from "medusa-interfaces"
|
||||||
|
|
||||||
|
class IdealAdyenService extends PaymentService {
|
||||||
|
static identifier = "ideal-adyen"
|
||||||
|
|
||||||
|
constructor({ adyenService }) {
|
||||||
|
super()
|
||||||
|
|
||||||
|
this.adyenService_ = adyenService
|
||||||
|
}
|
||||||
|
|
||||||
|
async getStatus(paymentData) {
|
||||||
|
return this.adyenService_.getStatus(paymentData)
|
||||||
|
}
|
||||||
|
|
||||||
|
async createPayment(data) {
|
||||||
|
return this.adyenService_.createPayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async authorizePayment(sessionData, context) {
|
||||||
|
return this.adyenService_.authorizePayment(sessionData, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
async retrievePayment(data) {
|
||||||
|
return this.adyenService_.retrievePayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPaymentData(data) {
|
||||||
|
return this.adyenService_.getPaymentData(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async updatePayment(data, _) {
|
||||||
|
return this.adyenService_.updatePayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async updatePaymentData(sessionData, update) {
|
||||||
|
return this.adyenService_.updatePaymentData(sessionData, update)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPaymentData(data) {
|
||||||
|
return this.adyenService_.getPaymentData(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async deletePayment(data) {
|
||||||
|
return this.adyenService_.deletePayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async capturePayment(data) {
|
||||||
|
return this.adyenService_.capturePayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async refundPayment(data, amountToRefund) {
|
||||||
|
return this.adyenService_.refundPayment(data, amountToRefund)
|
||||||
|
}
|
||||||
|
|
||||||
|
async cancelPayment(data) {
|
||||||
|
return this.adyenService_.cancelPayment(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default IdealAdyenService
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
import _ from "lodash"
|
|
||||||
import { PaymentService } from "medusa-interfaces"
|
|
||||||
|
|
||||||
class IdealAdyenService extends PaymentService {
|
|
||||||
static identifier = "idealAdyen"
|
|
||||||
|
|
||||||
constructor({ adyenService }) {
|
|
||||||
super()
|
|
||||||
|
|
||||||
this.adyenService_ = adyenService
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Status for Adyen payment.
|
|
||||||
* @param {Object} paymentData - payment method data from cart
|
|
||||||
* @returns {string} the status of the payment
|
|
||||||
*/
|
|
||||||
async getStatus(paymentData) {
|
|
||||||
const { resultCode } = paymentData
|
|
||||||
let status = "initial"
|
|
||||||
|
|
||||||
if (resultCode === "Authorised") {
|
|
||||||
status = "authorized"
|
|
||||||
}
|
|
||||||
|
|
||||||
return status
|
|
||||||
}
|
|
||||||
|
|
||||||
async createPayment(_) {
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
|
|
||||||
async authorizePayment(cart, paymentMethod) {
|
|
||||||
return this.adyenService_.authorizePayment(cart, paymentMethod)
|
|
||||||
}
|
|
||||||
|
|
||||||
async retrievePayment(data) {
|
|
||||||
return this.adyenService_.retrievePayment(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
async updatePayment(data, _) {
|
|
||||||
return this.adyenService_.updatePayment(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
async deletePayment(data) {
|
|
||||||
return this.adyenService_.deletePayment(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
async capturePayment(data) {
|
|
||||||
try {
|
|
||||||
return this.adyenService_.capturePayment(data)
|
|
||||||
} catch (error) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async refundPayment(data) {
|
|
||||||
try {
|
|
||||||
return this.adyenService_.refundPayment(data)
|
|
||||||
} catch (error) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async cancelPayment(data) {
|
|
||||||
try {
|
|
||||||
return this.adyenService_.cancelPayment(data)
|
|
||||||
} catch (error) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default IdealAdyenService
|
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import _ from "lodash"
|
||||||
|
import { PaymentService } from "medusa-interfaces"
|
||||||
|
|
||||||
|
class MobilePayAdyenService extends PaymentService {
|
||||||
|
static identifier = "mobilepay-adyen"
|
||||||
|
|
||||||
|
constructor({ adyenService }) {
|
||||||
|
super()
|
||||||
|
|
||||||
|
this.adyenService_ = adyenService
|
||||||
|
}
|
||||||
|
|
||||||
|
async getStatus(paymentData) {
|
||||||
|
return this.adyenService_.getStatus(paymentData)
|
||||||
|
}
|
||||||
|
|
||||||
|
async createPayment(data) {
|
||||||
|
return this.adyenService_.createPayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async authorizePayment(sessionData, context) {
|
||||||
|
return this.adyenService_.authorizePayment(sessionData, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
async retrievePayment(data) {
|
||||||
|
return this.adyenService_.retrievePayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async updatePayment(data, _) {
|
||||||
|
return this.adyenService_.updatePayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async updatePaymentData(sessionData, update) {
|
||||||
|
return this.adyenService_.updatePaymentData(sessionData, update)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPaymentData(data) {
|
||||||
|
return this.adyenService_.getPaymentData(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async deletePayment(data) {
|
||||||
|
return this.adyenService_.deletePayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async capturePayment(data) {
|
||||||
|
return this.adyenService_.capturePayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async refundPayment(data, amountToRefund) {
|
||||||
|
return this.adyenService_.refundPayment(data, amountToRefund)
|
||||||
|
}
|
||||||
|
|
||||||
|
async cancelPayment(data) {
|
||||||
|
return this.adyenService_.cancelPayment(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MobilePayAdyenService
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
import _ from "lodash"
|
|
||||||
import { PaymentService } from "medusa-interfaces"
|
|
||||||
|
|
||||||
class MobilePayAdyenService extends PaymentService {
|
|
||||||
static identifier = "mobilepayAdyen"
|
|
||||||
|
|
||||||
constructor({ adyenService }) {
|
|
||||||
super()
|
|
||||||
|
|
||||||
this.adyenService_ = adyenService
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Status for Adyen payment.
|
|
||||||
* @param {Object} paymentData - payment method data from cart
|
|
||||||
* @returns {string} the status of the payment
|
|
||||||
*/
|
|
||||||
async getStatus(paymentData) {
|
|
||||||
const { resultCode } = paymentData
|
|
||||||
// let status = "initial"
|
|
||||||
|
|
||||||
// if (resultCode === "Authorised") {
|
|
||||||
// status = "authorized"
|
|
||||||
// }
|
|
||||||
|
|
||||||
return "authorized"
|
|
||||||
}
|
|
||||||
|
|
||||||
async createPayment(_) {
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
|
|
||||||
async authorizePayment(cart, paymentMethod) {
|
|
||||||
return this.adyenService_.authorizePayment(cart, paymentMethod)
|
|
||||||
}
|
|
||||||
|
|
||||||
async retrievePayment(data) {
|
|
||||||
return this.adyenService_.retrievePayment(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
async updatePayment(data, _) {
|
|
||||||
return this.adyenService_.updatePayment(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
async deletePayment(data) {
|
|
||||||
return this.adyenService_.deletePayment(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
async capturePayment(data) {
|
|
||||||
try {
|
|
||||||
return this.adyenService_.capturePayment(data)
|
|
||||||
} catch (error) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async refundPayment(data) {
|
|
||||||
try {
|
|
||||||
return this.adyenService_.refundPayment(data)
|
|
||||||
} catch (error) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async cancelPayment(data) {
|
|
||||||
try {
|
|
||||||
return this.adyenService_.cancelPayment(data)
|
|
||||||
} catch (error) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MobilePayAdyenService
|
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import _ from "lodash"
|
||||||
|
import { PaymentService } from "medusa-interfaces"
|
||||||
|
|
||||||
|
class PayPalAdyenService extends PaymentService {
|
||||||
|
static identifier = "paypal-adyen"
|
||||||
|
|
||||||
|
constructor({ adyenService }) {
|
||||||
|
super()
|
||||||
|
|
||||||
|
this.adyenService_ = adyenService
|
||||||
|
}
|
||||||
|
|
||||||
|
async getStatus(paymentData) {
|
||||||
|
return this.adyenService_.getStatus(paymentData)
|
||||||
|
}
|
||||||
|
|
||||||
|
async createPayment(data) {
|
||||||
|
return this.adyenService_.createPayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async authorizePayment(sessionData, context) {
|
||||||
|
return this.adyenService_.authorizePayment(sessionData, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
async retrievePayment(data) {
|
||||||
|
return this.adyenService_.retrievePayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPaymentData(data) {
|
||||||
|
return this.adyenService_.getPaymentData(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async updatePayment(data, _) {
|
||||||
|
return this.adyenService_.updatePayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async updatePaymentData(sessionData, update) {
|
||||||
|
return this.adyenService_.updatePaymentData(sessionData, update)
|
||||||
|
}
|
||||||
|
|
||||||
|
async deletePayment(data) {
|
||||||
|
return this.adyenService_.deletePayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async capturePayment(data) {
|
||||||
|
return this.adyenService_.capturePayment(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async refundPayment(data, amountToRefund) {
|
||||||
|
return this.adyenService_.refundPayment(data, amountToRefund)
|
||||||
|
}
|
||||||
|
|
||||||
|
async cancelPayment(data) {
|
||||||
|
return this.adyenService_.cancelPayment(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PayPalAdyenService
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
import _ from "lodash"
|
|
||||||
import { PaymentService } from "medusa-interfaces"
|
|
||||||
|
|
||||||
class PayPalAdyenService extends PaymentService {
|
|
||||||
static identifier = "paypalAdyen"
|
|
||||||
|
|
||||||
constructor({ adyenService }) {
|
|
||||||
super()
|
|
||||||
|
|
||||||
this.adyenService_ = adyenService
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Status for Adyen payment.
|
|
||||||
* @param {Object} paymentData - payment method data from cart
|
|
||||||
* @returns {string} the status of the payment
|
|
||||||
*/
|
|
||||||
async getStatus(paymentData) {
|
|
||||||
const { resultCode } = paymentData
|
|
||||||
let status = "initial"
|
|
||||||
|
|
||||||
if (resultCode === "Authorised") {
|
|
||||||
status = "authorized"
|
|
||||||
}
|
|
||||||
|
|
||||||
return status
|
|
||||||
}
|
|
||||||
|
|
||||||
async createPayment(_) {
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
|
|
||||||
async authorizePayment(cart, paymentMethod) {
|
|
||||||
return this.adyenService_.authorizePayment(cart, paymentMethod)
|
|
||||||
}
|
|
||||||
|
|
||||||
async retrievePayment(data) {
|
|
||||||
return this.adyenService_.retrievePayment(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
async updatePayment(data, _) {
|
|
||||||
return this.adyenService_.updatePayment(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
async deletePayment(data) {
|
|
||||||
return this.adyenService_.deletePayment(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
async capturePayment(data) {
|
|
||||||
try {
|
|
||||||
return this.adyenService_.capturePayment(data)
|
|
||||||
} catch (error) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async refundPayment(data) {
|
|
||||||
try {
|
|
||||||
return this.adyenService_.refundPayment(data)
|
|
||||||
} catch (error) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async cancelPayment(data) {
|
|
||||||
try {
|
|
||||||
return this.adyenService_.cancelPayment(data)
|
|
||||||
} catch (error) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default PayPalAdyenService
|
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
class AdyenSubscriber {
|
||||||
|
constructor(container) {
|
||||||
|
/** @private @const {CartService} */
|
||||||
|
this.cartService_ = container.cartService
|
||||||
|
|
||||||
|
/** @private @const {OrderService} */
|
||||||
|
this.orderService_ = container.orderService
|
||||||
|
|
||||||
|
/** @private @const {EventBusService} */
|
||||||
|
this.eventBus_ = container.eventBusService
|
||||||
|
|
||||||
|
/** @private @const {PaymentRepository} */
|
||||||
|
this.paymentRepository_ = container.paymentRepository
|
||||||
|
|
||||||
|
this.manager_ = container.manager
|
||||||
|
|
||||||
|
this.eventBus_.subscribe(
|
||||||
|
"adyen.notification_received",
|
||||||
|
async (notification) => this.handleAdyenNotification(notification)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Webhook handler for Adyen payment.
|
||||||
|
* @param {object} notification - webhook notification object
|
||||||
|
* @returns {string} the status of the payment intent
|
||||||
|
*/
|
||||||
|
async handleAdyenNotification(notification) {
|
||||||
|
switch (true) {
|
||||||
|
case notification.success === "true" &&
|
||||||
|
notification.eventCode === "AUTHORISATION": {
|
||||||
|
this.handleAuthorization_(notification)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case notification.success === "false" &&
|
||||||
|
notification.eventCode === "AUTHORISATION": {
|
||||||
|
this.handleFailedAuthorization_(notification)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case notification.success === "true" &&
|
||||||
|
notification.eventCode === "CAPTURE_FAILED": {
|
||||||
|
this.handleFailedCapture_(notification)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case notification.success === "false" &&
|
||||||
|
notification.eventCode === "CAPTURE": {
|
||||||
|
this.handleFailedCapture_(notification)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case notification.success === "false" &&
|
||||||
|
notification.eventCode === "REFUND": {
|
||||||
|
this.handleFailedRefund_(notification)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleFailedAuthorization_(notification) {
|
||||||
|
const cartId = notification.additionalData["metadata.cart_id"]
|
||||||
|
const cart = await this.cartService_.retrieve(cartId, {
|
||||||
|
relations: ["payment_sessions"],
|
||||||
|
})
|
||||||
|
|
||||||
|
const { payment_session } = cart
|
||||||
|
|
||||||
|
const updated = {
|
||||||
|
...payment_session,
|
||||||
|
status: "error",
|
||||||
|
data: {
|
||||||
|
...payment_session.data,
|
||||||
|
pspReference: notification.pspReference,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.cartService_.updatePaymentSession(cart.id, updated)
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleAuthorization_(notification) {
|
||||||
|
const cartId = notification.additionalData["metadata.cart_id"]
|
||||||
|
const paymentRepository = this.manager_.getCustomRepository(
|
||||||
|
this.paymentRepository_
|
||||||
|
)
|
||||||
|
|
||||||
|
// We need to ensure, that an order is created in situations, where the
|
||||||
|
// customer might have closed their browser prior to order creation
|
||||||
|
try {
|
||||||
|
const orderPayment = await paymentRepository.findOne({
|
||||||
|
where: { cart_id: cartId },
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!orderPayment) {
|
||||||
|
throw new Error("Payment not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedPayment = {
|
||||||
|
...orderPayment,
|
||||||
|
data: {
|
||||||
|
...orderPayment.data,
|
||||||
|
resultCode: "Authorised",
|
||||||
|
pspReference: notification.pspReference,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.paymentRepository_.save(updatedPayment)
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
await this.manager_.transaction(async (manager) => {
|
||||||
|
const session = {
|
||||||
|
pspReference: notification.pspReference,
|
||||||
|
resultCode: "Authorised",
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.cartService_
|
||||||
|
.withTransaction(manager)
|
||||||
|
.updatePaymentSession(cartId, session)
|
||||||
|
|
||||||
|
await this.cartService_
|
||||||
|
.withTransaction(manager)
|
||||||
|
.authorizePayment(cartId)
|
||||||
|
|
||||||
|
await this.orderService_.withTransaction(manager).createFromCart(cartId)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleFailedCapture_(notification) {
|
||||||
|
const cartId = notification.additionalData["metadata.cart_id"]
|
||||||
|
|
||||||
|
const order = await this.orderService_.retrieveByCartId(cartId)
|
||||||
|
|
||||||
|
await this.orderService_.update(order.id, {
|
||||||
|
payment_status: "requires_action",
|
||||||
|
})
|
||||||
|
|
||||||
|
await this.eventBus_.emit("order.payment_capture_failed", {
|
||||||
|
order,
|
||||||
|
error: new Error(`Adyen payment capture: ${notification.reason}`),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleFailedRefund_(notification) {
|
||||||
|
const cartId = notification.additionalData["metadata.cart_id"]
|
||||||
|
|
||||||
|
const order = await this.orderService_.retrieveByCartId(cartId)
|
||||||
|
|
||||||
|
await this.orderService_.update(order.id, {
|
||||||
|
payment_status: "requires_action",
|
||||||
|
})
|
||||||
|
|
||||||
|
await this.eventBus_.emit("order.refund_failed", {
|
||||||
|
order,
|
||||||
|
error: new Error(`Adyen payment capture: ${notification.reason}`),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AdyenSubscriber
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-payment-klarna",
|
"name": "medusa-payment-klarna",
|
||||||
"version": "1.0.14",
|
"version": "1.0.13-alpha.176+0646bd3",
|
||||||
"description": "Klarna Payment provider for Medusa Commerce",
|
"description": "Klarna Payment provider for Medusa Commerce",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -40,8 +40,8 @@
|
|||||||
"axios": "^0.21.0",
|
"axios": "^0.21.0",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"medusa-core-utils": "^1.0.11",
|
"medusa-core-utils": "^1.0.12-alpha.787+0646bd3",
|
||||||
"medusa-test-utils": "^1.0.13"
|
"medusa-test-utils": "^1.0.12-alpha.176+0646bd3"
|
||||||
},
|
},
|
||||||
"gitHead": "3cc7cbe5124cbcbb75f6e1435db4dcfaa2a60408"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -2,13 +2,17 @@ import { IdMap } from "medusa-test-utils"
|
|||||||
|
|
||||||
export const carts = {
|
export const carts = {
|
||||||
frCart: {
|
frCart: {
|
||||||
_id: IdMap.getId("fr-cart"),
|
id: IdMap.getId("fr-cart"),
|
||||||
email: "lebron@james.com",
|
email: "lebron@james.com",
|
||||||
title: "test",
|
title: "test",
|
||||||
|
region: {
|
||||||
|
tax_rate: 2500,
|
||||||
|
currency_code: "eur",
|
||||||
|
},
|
||||||
region_id: IdMap.getId("region-france"),
|
region_id: IdMap.getId("region-france"),
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
_id: IdMap.getId("line"),
|
id: IdMap.getId("line"),
|
||||||
title: "merge line",
|
title: "merge line",
|
||||||
description: "This is a new line",
|
description: "This is a new line",
|
||||||
thumbnail: "test-img-yeah.com/thumb",
|
thumbnail: "test-img-yeah.com/thumb",
|
||||||
@@ -16,20 +20,20 @@ export const carts = {
|
|||||||
{
|
{
|
||||||
unit_price: 8,
|
unit_price: 8,
|
||||||
variant: {
|
variant: {
|
||||||
_id: IdMap.getId("eur-8-us-10"),
|
id: IdMap.getId("eur-8-us-10"),
|
||||||
},
|
},
|
||||||
product: {
|
product: {
|
||||||
_id: IdMap.getId("product"),
|
id: IdMap.getId("product"),
|
||||||
},
|
},
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
unit_price: 10,
|
unit_price: 10,
|
||||||
variant: {
|
variant: {
|
||||||
_id: IdMap.getId("eur-10-us-12"),
|
id: IdMap.getId("eur-10-us-12"),
|
||||||
},
|
},
|
||||||
product: {
|
product: {
|
||||||
_id: IdMap.getId("product"),
|
id: IdMap.getId("product"),
|
||||||
},
|
},
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
},
|
},
|
||||||
@@ -37,17 +41,17 @@ export const carts = {
|
|||||||
quantity: 10,
|
quantity: 10,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
_id: IdMap.getId("existingLine"),
|
id: IdMap.getId("existingLine"),
|
||||||
title: "merge line",
|
title: "merge line",
|
||||||
description: "This is a new line",
|
description: "This is a new line",
|
||||||
thumbnail: "test-img-yeah.com/thumb",
|
thumbnail: "test-img-yeah.com/thumb",
|
||||||
content: {
|
content: {
|
||||||
unit_price: 10,
|
unit_price: 10,
|
||||||
variant: {
|
variant: {
|
||||||
_id: IdMap.getId("eur-10-us-12"),
|
id: IdMap.getId("eur-10-us-12"),
|
||||||
},
|
},
|
||||||
product: {
|
product: {
|
||||||
_id: IdMap.getId("product"),
|
id: IdMap.getId("product"),
|
||||||
},
|
},
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
},
|
},
|
||||||
@@ -56,13 +60,16 @@ export const carts = {
|
|||||||
],
|
],
|
||||||
shipping_methods: [
|
shipping_methods: [
|
||||||
{
|
{
|
||||||
_id: IdMap.getId("freeShipping"),
|
id: IdMap.getId("freeShipping"),
|
||||||
|
data: {
|
||||||
|
name: "test",
|
||||||
|
},
|
||||||
profile_id: "default_profile",
|
profile_id: "default_profile",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
shipping_options: [
|
shipping_options: [
|
||||||
{
|
{
|
||||||
_id: IdMap.getId("freeShipping"),
|
id: IdMap.getId("freeShipping"),
|
||||||
profile_id: "default_profile",
|
profile_id: "default_profile",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -87,7 +94,7 @@ export const carts = {
|
|||||||
discounts: [
|
discounts: [
|
||||||
{
|
{
|
||||||
code: "MEDUSA_FREE",
|
code: "MEDUSA_FREE",
|
||||||
discount_rule: {
|
rule: {
|
||||||
type: "percent",
|
type: "percent",
|
||||||
value: 20,
|
value: 20,
|
||||||
allocation: "item",
|
allocation: "item",
|
||||||
|
|||||||
@@ -3,38 +3,107 @@ export default async (req, res) => {
|
|||||||
const { shipping_address, merchant_data } = req.body
|
const { shipping_address, merchant_data } = req.body
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const manager = req.scope.resolve("manager")
|
||||||
const cartService = req.scope.resolve("cartService")
|
const cartService = req.scope.resolve("cartService")
|
||||||
const klarnaProviderService = req.scope.resolve("pp_klarna")
|
const klarnaProviderService = req.scope.resolve("pp_klarna")
|
||||||
const shippingProfileService = req.scope.resolve("shippingProfileService")
|
const shippingProfileService = req.scope.resolve("shippingProfileService")
|
||||||
|
|
||||||
const cart = await cartService.retrieve(merchant_data)
|
const result = await manager.transaction("SERIALIZABLE", async (m) => {
|
||||||
|
const cart = await cartService.retrieve(merchant_data, {
|
||||||
|
select: ["subtotal"],
|
||||||
|
relations: [
|
||||||
|
"shipping_address",
|
||||||
|
"billing_address",
|
||||||
|
"region",
|
||||||
|
"items",
|
||||||
|
"shipping_methods",
|
||||||
|
"shipping_methods.shipping_option",
|
||||||
|
"items.variant",
|
||||||
|
"items.variant.product",
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
if (shipping_address) {
|
if (shipping_address) {
|
||||||
const updatedAddress = {
|
const shippingAddress = {
|
||||||
first_name: shipping_address.given_name,
|
...cart.shipping_address,
|
||||||
last_name: shipping_address.family_name,
|
first_name: shipping_address.given_name,
|
||||||
address_1: shipping_address.street_address,
|
last_name: shipping_address.family_name,
|
||||||
address_2: shipping_address.street_address2,
|
address_1: shipping_address.street_address,
|
||||||
city: shipping_address.city,
|
address_2: shipping_address.street_address2,
|
||||||
country_code: shipping_address.country.toUpperCase(),
|
city: shipping_address.city,
|
||||||
postal_code: shipping_address.postal_code,
|
country_code: shipping_address.country,
|
||||||
phone: shipping_address.phone
|
postal_code: shipping_address.postal_code,
|
||||||
|
phone: shipping_address.phone,
|
||||||
|
}
|
||||||
|
|
||||||
|
let billingAddress = {
|
||||||
|
first_name: shipping_address.given_name,
|
||||||
|
last_name: shipping_address.family_name,
|
||||||
|
address_1: shipping_address.street_address,
|
||||||
|
address_2: shipping_address.street_address2,
|
||||||
|
city: shipping_address.city,
|
||||||
|
country_code: shipping_address.country,
|
||||||
|
postal_code: shipping_address.postal_code,
|
||||||
|
phone: shipping_address.phone,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cart.billing_address) {
|
||||||
|
billingAddress = {
|
||||||
|
...billingAddress,
|
||||||
|
...cart.billing_address,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await cartService.update(cart.id, {
|
||||||
|
shipping_address: shippingAddress,
|
||||||
|
billing_address: billingAddress,
|
||||||
|
email: shipping_address.email,
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingOptions = await shippingProfileService.fetchCartOptions(
|
||||||
|
cart
|
||||||
|
)
|
||||||
|
|
||||||
|
if (shippingOptions?.length) {
|
||||||
|
const option = shippingOptions.find(
|
||||||
|
(o) => o.data && !o.data.require_drop_point
|
||||||
|
)
|
||||||
|
await cartService
|
||||||
|
.withTransaction(m)
|
||||||
|
.addShippingMethod(cart.id, option.id, option.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch and return updated Klarna order
|
||||||
|
const updatedCart = await cartService
|
||||||
|
.withTransaction(m)
|
||||||
|
.retrieve(cart.id, {
|
||||||
|
select: [
|
||||||
|
"gift_card_total",
|
||||||
|
"subtotal",
|
||||||
|
"total",
|
||||||
|
"tax_total",
|
||||||
|
"discount_total",
|
||||||
|
"subtotal",
|
||||||
|
],
|
||||||
|
relations: [
|
||||||
|
"shipping_address",
|
||||||
|
"billing_address",
|
||||||
|
"region",
|
||||||
|
"shipping_methods",
|
||||||
|
"shipping_methods.shipping_option",
|
||||||
|
"items",
|
||||||
|
"items.variant",
|
||||||
|
"items.variant.product",
|
||||||
|
],
|
||||||
|
})
|
||||||
|
return klarnaProviderService.cartToKlarnaOrder(updatedCart)
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
await cartService.updateShippingAddress(cart._id, updatedAddress)
|
if (result) {
|
||||||
await cartService.updateBillingAddress(cart._id, updatedAddress)
|
res.json(result)
|
||||||
await cartService.updateEmail(cart._id, shipping_address.email)
|
|
||||||
|
|
||||||
const shippingOptions = await shippingProfileService.fetchCartOptions(cart)
|
|
||||||
if (shippingOptions.length === 1) {
|
|
||||||
const option = shippingOptions[0]
|
|
||||||
await cartService.addShippingMethod(cart._id, option._id, option.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch and return updated Klarna order
|
|
||||||
const updatedCart = await cartService.retrieve(cart._id)
|
|
||||||
const order = await klarnaProviderService.cartToKlarnaOrder(updatedCart)
|
|
||||||
res.json(order)
|
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
res.sendStatus(400)
|
res.sendStatus(400)
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ export default async (req, res) => {
|
|||||||
const { klarna_order_id } = req.query
|
const { klarna_order_id } = req.query
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const cartService = req.scope.resolve("cartService")
|
|
||||||
const orderService = req.scope.resolve("orderService")
|
const orderService = req.scope.resolve("orderService")
|
||||||
const klarnaProviderService = req.scope.resolve("pp_klarna")
|
const klarnaProviderService = req.scope.resolve("pp_klarna")
|
||||||
|
|
||||||
@@ -13,22 +12,9 @@ export default async (req, res) => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const cartId = klarnaOrder.merchant_data
|
const cartId = klarnaOrder.merchant_data
|
||||||
try {
|
const order = await orderService.retrieveByCartId(cartId)
|
||||||
const order = await orderService.retrieveByCartId(cartId)
|
|
||||||
await klarnaProviderService.acknowledgeOrder(
|
await klarnaProviderService.acknowledgeOrder(klarnaOrder.order_id, order.id)
|
||||||
klarnaOrder.order_id,
|
|
||||||
order._id
|
|
||||||
)
|
|
||||||
} catch (err) {
|
|
||||||
if (err.type === MedusaError.Types.NOT_FOUND) {
|
|
||||||
const cart = await cartService.retrieve(cartId)
|
|
||||||
const order = await orderService.createFromCart(cart)
|
|
||||||
await klarnaProviderService.acknowledgeOrder(
|
|
||||||
klarnaOrder.order_id,
|
|
||||||
order._id
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res.sendStatus(200)
|
res.sendStatus(200)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -7,19 +7,57 @@ export default async (req, res) => {
|
|||||||
const klarnaProviderService = req.scope.resolve("pp_klarna")
|
const klarnaProviderService = req.scope.resolve("pp_klarna")
|
||||||
const shippingProfileService = req.scope.resolve("shippingProfileService")
|
const shippingProfileService = req.scope.resolve("shippingProfileService")
|
||||||
|
|
||||||
const cart = await cartService.retrieve(merchant_data)
|
const cart = await cartService.retrieve(merchant_data, {
|
||||||
const shippingOptions = await shippingProfileService.fetchCartOptions(cart)
|
select: ["subtotal"],
|
||||||
|
relations: [
|
||||||
|
"shipping_address",
|
||||||
|
"billing_address",
|
||||||
|
"region",
|
||||||
|
"shipping_methods",
|
||||||
|
"shipping_methods.shipping_option",
|
||||||
|
"items",
|
||||||
|
"items.variant",
|
||||||
|
"items.variant.product",
|
||||||
|
],
|
||||||
|
})
|
||||||
|
let shippingOptions = await shippingProfileService.fetchCartOptions(cart)
|
||||||
|
|
||||||
|
shippingOptions = shippingOptions.filter(
|
||||||
|
(so) => !so.data?.require_drop_point
|
||||||
|
)
|
||||||
|
|
||||||
const ids = selected_shipping_option.id.split(".")
|
const ids = selected_shipping_option.id.split(".")
|
||||||
await Promise.all(ids.map(async id => {
|
await Promise.all(
|
||||||
const option = shippingOptions.find(({ _id }) => _id.equals(id))
|
ids.map(async (id) => {
|
||||||
|
const option = shippingOptions.find((so) => so.id === id)
|
||||||
|
|
||||||
if (option) {
|
if (option) {
|
||||||
await cartService.addShippingMethod(cart._id, option._id, option.data)
|
await cartService.addShippingMethod(cart.id, option.id, option.data)
|
||||||
}
|
}
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const newCart = await cartService.retrieve(cart.id, {
|
||||||
|
select: [
|
||||||
|
"gift_card_total",
|
||||||
|
"subtotal",
|
||||||
|
"total",
|
||||||
|
"tax_total",
|
||||||
|
"discount_total",
|
||||||
|
"subtotal",
|
||||||
|
],
|
||||||
|
relations: [
|
||||||
|
"shipping_address",
|
||||||
|
"billing_address",
|
||||||
|
"shipping_methods",
|
||||||
|
"shipping_methods.shipping_option",
|
||||||
|
"region",
|
||||||
|
"items",
|
||||||
|
"items.variant",
|
||||||
|
"items.variant.product",
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
const newCart = await cartService.retrieve(cart._id)
|
|
||||||
const order = await klarnaProviderService.cartToKlarnaOrder(newCart)
|
const order = await klarnaProviderService.cartToKlarnaOrder(newCart)
|
||||||
res.json(order)
|
res.json(order)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
jest.unmock("axios")
|
||||||
|
import axios from "axios"
|
||||||
import MockAdapter from "axios-mock-adapter"
|
import MockAdapter from "axios-mock-adapter"
|
||||||
|
|
||||||
import KlarnaProviderService from "../klarna-provider"
|
import KlarnaProviderService from "../klarna-provider"
|
||||||
@@ -151,7 +153,6 @@ describe("KlarnaProviderService", () => {
|
|||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
order_id: "123456789",
|
order_id: "123456789",
|
||||||
order_amount: 1000,
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -176,15 +177,19 @@ describe("KlarnaProviderService", () => {
|
|||||||
mockServer
|
mockServer
|
||||||
.onPost("/ordermanagement/v1/orders/123456789/cancel")
|
.onPost("/ordermanagement/v1/orders/123456789/cancel")
|
||||||
.reply(() => {
|
.reply(() => {
|
||||||
return [200]
|
return [200, { order_id: "123456789" }]
|
||||||
})
|
})
|
||||||
|
|
||||||
it("returns order id", async () => {
|
mockServer.onGet("/ordermanagement/v1/orders/123456789").reply(() => {
|
||||||
|
return [200, { order_id: "123456789" }]
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns order", async () => {
|
||||||
result = await klarnaProviderService.cancelPayment({
|
result = await klarnaProviderService.cancelPayment({
|
||||||
order_id: "123456789",
|
data: { order_id: "123456789" },
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(result).toEqual("123456789")
|
expect(result).toEqual({ order_id: "123456789" })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -289,15 +294,19 @@ describe("KlarnaProviderService", () => {
|
|||||||
mockServer
|
mockServer
|
||||||
.onPost("/ordermanagement/v1/orders/123456789/captures")
|
.onPost("/ordermanagement/v1/orders/123456789/captures")
|
||||||
.reply(() => {
|
.reply(() => {
|
||||||
return [200]
|
return [200, { order_id: "123456789" }]
|
||||||
})
|
})
|
||||||
|
|
||||||
it("returns order id", async () => {
|
mockServer.onGet("/ordermanagement/v1/orders/123456789").reply(() => {
|
||||||
|
return [200, { order_id: "123456789" }]
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns order", async () => {
|
||||||
result = await klarnaProviderService.capturePayment({
|
result = await klarnaProviderService.capturePayment({
|
||||||
order_id: "123456789",
|
data: { order_id: "123456789" },
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(result).toEqual("123456789")
|
expect(result).toEqual({ order_id: "123456789" })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -322,18 +331,22 @@ describe("KlarnaProviderService", () => {
|
|||||||
mockServer
|
mockServer
|
||||||
.onPost("/ordermanagement/v1/orders/123456789/refunds")
|
.onPost("/ordermanagement/v1/orders/123456789/refunds")
|
||||||
.reply(() => {
|
.reply(() => {
|
||||||
return [200]
|
return [200, { order_id: "123456789" }]
|
||||||
})
|
})
|
||||||
|
|
||||||
it("returns order id", async () => {
|
mockServer.onGet("/ordermanagement/v1/orders/123456789").reply(() => {
|
||||||
|
return [200, { order_id: "123456789" }]
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns order", async () => {
|
||||||
result = await klarnaProviderService.refundPayment(
|
result = await klarnaProviderService.refundPayment(
|
||||||
{
|
{
|
||||||
order_id: "123456789",
|
data: { order_id: "123456789" },
|
||||||
},
|
},
|
||||||
1000
|
1000
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(result).toEqual("123456789")
|
expect(result).toEqual({ order_id: "123456789" })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -5,14 +5,26 @@ import { PaymentService } from "medusa-interfaces"
|
|||||||
class KlarnaProviderService extends PaymentService {
|
class KlarnaProviderService extends PaymentService {
|
||||||
static identifier = "klarna"
|
static identifier = "klarna"
|
||||||
|
|
||||||
constructor(
|
constructor({ shippingProfileService }, options) {
|
||||||
{ shippingProfileService, totalsService, regionService },
|
|
||||||
options
|
|
||||||
) {
|
|
||||||
super()
|
super()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Required Klarna options:
|
||||||
|
* {
|
||||||
|
* backend_url: "",
|
||||||
|
* url: "",
|
||||||
|
* user: "",
|
||||||
|
* password: "",
|
||||||
|
* merchant_urls: {
|
||||||
|
* terms: ``,
|
||||||
|
* checkout: ``,
|
||||||
|
* confirmation: ``,
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
this.options_ = options
|
this.options_ = options
|
||||||
|
|
||||||
|
/** @private @const {Klarna} */
|
||||||
this.klarna_ = axios.create({
|
this.klarna_ = axios.create({
|
||||||
baseURL: options.url,
|
baseURL: options.url,
|
||||||
auth: {
|
auth: {
|
||||||
@@ -27,54 +39,36 @@ class KlarnaProviderService extends PaymentService {
|
|||||||
|
|
||||||
this.backendUrl_ = options.backend_url
|
this.backendUrl_ = options.backend_url
|
||||||
|
|
||||||
this.totalsService_ = totalsService
|
/** @private @const {ShippingProfileService} */
|
||||||
|
|
||||||
this.regionService_ = regionService
|
|
||||||
|
|
||||||
this.shippingProfileService_ = shippingProfileService
|
this.shippingProfileService_ = shippingProfileService
|
||||||
}
|
}
|
||||||
|
|
||||||
async lineItemsToOrderLines_(cart, taxRate) {
|
async lineItemsToOrderLines_(cart, taxRate) {
|
||||||
let order_lines = []
|
let order_lines = []
|
||||||
|
|
||||||
|
const tax = taxRate / 100
|
||||||
|
|
||||||
cart.items.forEach((item) => {
|
cart.items.forEach((item) => {
|
||||||
// For bundles, we create an order line for each item in the bundle
|
// Withdraw discount from the total item amount
|
||||||
if (Array.isArray(item.content)) {
|
const quantity = item.quantity
|
||||||
item.content.forEach((c) => {
|
const unit_price = item.unit_price * (tax + 1)
|
||||||
const total_amount = c.unit_price * c.quantity * (taxRate + 1)
|
const total_amount = unit_price * quantity
|
||||||
const total_tax_amount = total_amount * taxRate
|
const total_tax_amount = total_amount * (tax / (1 + tax))
|
||||||
|
|
||||||
order_lines.push({
|
order_lines.push({
|
||||||
name: item.title,
|
name: item.title,
|
||||||
unit_price: c.unit_price,
|
tax_rate: tax * 10000,
|
||||||
quantity: c.quantity,
|
quantity,
|
||||||
tax_rate: taxRate * 10000,
|
unit_price,
|
||||||
total_amount,
|
total_amount,
|
||||||
total_tax_amount,
|
total_tax_amount,
|
||||||
})
|
})
|
||||||
})
|
|
||||||
} else {
|
|
||||||
// Withdraw discount from the total item amount
|
|
||||||
const quantity = item.quantity
|
|
||||||
const unit_price = item.content.unit_price * 100 * (taxRate + 1)
|
|
||||||
const total_amount = unit_price * quantity
|
|
||||||
const total_tax_amount = total_amount * (taxRate / (1 + taxRate))
|
|
||||||
|
|
||||||
order_lines.push({
|
|
||||||
name: item.title,
|
|
||||||
tax_rate: taxRate * 10000,
|
|
||||||
quantity,
|
|
||||||
unit_price,
|
|
||||||
total_amount,
|
|
||||||
total_tax_amount,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if (cart.shipping_methods.length) {
|
if (cart.shipping_methods.length) {
|
||||||
const { name, price } = cart.shipping_methods.reduce(
|
const { name, price } = cart.shipping_methods.reduce(
|
||||||
(acc, next) => {
|
(acc, next) => {
|
||||||
acc.name = [...acc.name, next.name]
|
acc.name = [...acc.name, next.data.name]
|
||||||
acc.price += next.price
|
acc.price += next.price
|
||||||
return acc
|
return acc
|
||||||
},
|
},
|
||||||
@@ -85,10 +79,10 @@ class KlarnaProviderService extends PaymentService {
|
|||||||
name: name.join(" + "),
|
name: name.join(" + "),
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
type: "shipping_fee",
|
type: "shipping_fee",
|
||||||
unit_price: price * (1 + taxRate) * 100,
|
unit_price: price * (1 + tax),
|
||||||
tax_rate: taxRate * 10000,
|
tax_rate: tax * 10000,
|
||||||
total_amount: price * (1 + taxRate) * 100,
|
total_amount: price * (1 + tax),
|
||||||
total_tax_amount: price * taxRate * 100,
|
total_tax_amount: price * tax,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,28 +92,39 @@ class KlarnaProviderService extends PaymentService {
|
|||||||
async cartToKlarnaOrder(cart) {
|
async cartToKlarnaOrder(cart) {
|
||||||
let order = {
|
let order = {
|
||||||
// Cart id is stored, such that we can use it for hooks
|
// Cart id is stored, such that we can use it for hooks
|
||||||
merchant_data: cart._id,
|
merchant_data: cart.id,
|
||||||
// TODO: Investigate if other locales are needed
|
|
||||||
locale: "en-US",
|
locale: "en-US",
|
||||||
}
|
}
|
||||||
|
|
||||||
const { tax_rate, currency_code } = await this.regionService_.retrieve(
|
const { region, gift_card_total, discount_total, tax_total, total } = cart
|
||||||
cart.region_id
|
|
||||||
)
|
|
||||||
|
|
||||||
order.order_lines = await this.lineItemsToOrderLines_(cart, tax_rate)
|
const taxRate = region.tax_rate / 100
|
||||||
|
|
||||||
const discount = (await this.totalsService_.getDiscountTotal(cart)) * 100
|
order.order_lines = await this.lineItemsToOrderLines_(cart, region.tax_rate)
|
||||||
if (discount) {
|
|
||||||
|
if (discount_total) {
|
||||||
order.order_lines.push({
|
order.order_lines.push({
|
||||||
name: `Discount`,
|
name: `Discount`,
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
type: "discount",
|
type: "discount",
|
||||||
unit_price: 0,
|
unit_price: 0,
|
||||||
total_discount_amount: discount * (1 + tax_rate),
|
total_discount_amount: discount_total * (1 + taxRate),
|
||||||
tax_rate: tax_rate * 10000,
|
tax_rate: taxRate * 10000,
|
||||||
total_amount: -discount * (1 + tax_rate),
|
total_amount: -discount_total * (1 + taxRate),
|
||||||
total_tax_amount: -discount * tax_rate,
|
total_tax_amount: -discount_total * taxRate,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gift_card_total) {
|
||||||
|
order.order_lines.push({
|
||||||
|
name: `Gift Card`,
|
||||||
|
quantity: 1,
|
||||||
|
type: "gift_card",
|
||||||
|
unit_price: 0,
|
||||||
|
total_discount_amount: gift_card_total * (1 + taxRate),
|
||||||
|
tax_rate: taxRate * 10000,
|
||||||
|
total_amount: -gift_card_total * (1 + taxRate),
|
||||||
|
total_tax_amount: -gift_card_total * taxRate,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,20 +139,19 @@ class KlarnaProviderService extends PaymentService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Check if country matches ISO
|
const hasCountry =
|
||||||
if (
|
!_.isEmpty(cart.shipping_address) && cart.shipping_address.country_code
|
||||||
!_.isEmpty(cart.shipping_address) &&
|
|
||||||
cart.shipping_address.country_code
|
if (hasCountry) {
|
||||||
) {
|
order.purchase_country = cart.shipping_address.country_code.toUpperCase()
|
||||||
order.purchase_country = cart.shipping_address.country_code
|
|
||||||
} else {
|
} else {
|
||||||
// Defaults to Sweden
|
// Defaults to Sweden
|
||||||
order.purchase_country = "SE"
|
order.purchase_country = "SE"
|
||||||
}
|
}
|
||||||
order.order_amount = (await this.totalsService_.getTotal(cart)) * 100
|
|
||||||
order.order_tax_amount = (await this.totalsService_.getTaxTotal(cart)) * 100
|
order.order_amount = total
|
||||||
// TODO: Check if currency matches ISO
|
order.order_tax_amount = tax_total
|
||||||
order.purchase_currency = currency_code
|
order.purchase_currency = region.currency_code.toUpperCase()
|
||||||
|
|
||||||
order.merchant_urls = {
|
order.merchant_urls = {
|
||||||
terms: this.options_.merchant_urls.terms,
|
terms: this.options_.merchant_urls.terms,
|
||||||
@@ -159,20 +163,24 @@ class KlarnaProviderService extends PaymentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cart.shipping_address && cart.shipping_address.first_name) {
|
if (cart.shipping_address && cart.shipping_address.first_name) {
|
||||||
const shippingOptions = await this.shippingProfileService_.fetchCartOptions(
|
let shippingOptions = await this.shippingProfileService_.fetchCartOptions(
|
||||||
cart
|
cart
|
||||||
)
|
)
|
||||||
|
|
||||||
|
shippingOptions = shippingOptions.filter(
|
||||||
|
(so) => !so.data?.require_drop_point
|
||||||
|
)
|
||||||
|
|
||||||
// If the cart does not have shipping methods yet, preselect one from
|
// If the cart does not have shipping methods yet, preselect one from
|
||||||
// shipping_options and set the selected shipping method
|
// shipping_options and set the selected shipping method
|
||||||
if (cart.shipping_methods.length) {
|
if (cart.shipping_methods.length) {
|
||||||
const shipping_method = cart.shipping_methods[0]
|
const shipping_method = cart.shipping_methods[0]
|
||||||
order.selected_shipping_option = {
|
order.selected_shipping_option = {
|
||||||
id: shipping_method._id,
|
id: shipping_method.shipping_option.id,
|
||||||
name: shipping_method.name,
|
name: shipping_method.shipping_option.name,
|
||||||
price: shipping_method.price * (1 + tax_rate) * 100,
|
price: shipping_method.price * (1 + taxRate),
|
||||||
tax_amount: shipping_method.price * tax_rate * 100,
|
tax_amount: shipping_method.price * taxRate,
|
||||||
tax_rate: tax_rate * 10000,
|
tax_rate: taxRate * 10000,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,20 +193,25 @@ class KlarnaProviderService extends PaymentService {
|
|||||||
return acc
|
return acc
|
||||||
}, {})
|
}, {})
|
||||||
|
|
||||||
let f = (a, b) =>
|
// Helper function that calculates the cartesian product of multiple arrays
|
||||||
[].concat(...a.map((a) => b.map((b) => [].concat(a, b))))
|
// Don't touch :D
|
||||||
let cartesian = (a, b, ...c) => (b ? cartesian(f(a, b), ...c) : a)
|
// From: https://stackoverflow.com/questions/12303989/cartesian-product-of-multiple-arrays-in-javascript
|
||||||
|
const f = (a, b) =>
|
||||||
|
[].concat(...a.map((d) => b.map((e) => [].concat(d, e))))
|
||||||
|
const cartesian = (a, b, ...c) => (b ? cartesian(f(a, b), ...c) : a)
|
||||||
|
|
||||||
const methods = Object.keys(partitioned).map((k) => partitioned[k])
|
const methods = Object.keys(partitioned).map((k) => partitioned[k])
|
||||||
const combinations = cartesian(...methods)
|
const combinations = cartesian(...methods)
|
||||||
|
|
||||||
|
// Use the cartesian product of shipping methods to generate correct
|
||||||
|
// format for the Klarna Widget
|
||||||
order.shipping_options = combinations.map((combination) => {
|
order.shipping_options = combinations.map((combination) => {
|
||||||
combination = Array.isArray(combination) ? combination : [combination]
|
combination = Array.isArray(combination) ? combination : [combination]
|
||||||
const details = combination.reduce(
|
const details = combination.reduce(
|
||||||
(acc, next) => {
|
(acc, next) => {
|
||||||
acc.id = [...acc.id, next._id]
|
acc.id = [...acc.id, next.id]
|
||||||
acc.name = [...acc.name, next.name]
|
acc.name = [...acc.name, next.name]
|
||||||
acc.price += next.price
|
acc.price += next.amount
|
||||||
return acc
|
return acc
|
||||||
},
|
},
|
||||||
{ id: [], name: [], price: 0 }
|
{ id: [], name: [], price: 0 }
|
||||||
@@ -207,10 +220,9 @@ class KlarnaProviderService extends PaymentService {
|
|||||||
return {
|
return {
|
||||||
id: details.id.join("."),
|
id: details.id.join("."),
|
||||||
name: details.name.join(" + "),
|
name: details.name.join(" + "),
|
||||||
price: details.price * (1 + tax_rate) * 100,
|
price: details.price * (1 + taxRate),
|
||||||
tax_amount: details.price * tax_rate * 100,
|
tax_amount: details.price * taxRate,
|
||||||
tax_rate: tax_rate * 10000,
|
tax_rate: taxRate * 10000,
|
||||||
preselected: combinations.length === 1,
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -224,20 +236,18 @@ class KlarnaProviderService extends PaymentService {
|
|||||||
* @returns {string} the status of the Klarna order
|
* @returns {string} the status of the Klarna order
|
||||||
*/
|
*/
|
||||||
async getStatus(paymentData) {
|
async getStatus(paymentData) {
|
||||||
try {
|
const { order_id } = paymentData
|
||||||
const { order_id } = paymentData
|
const { data: order } = await this.klarna_.get(
|
||||||
const { data: order } = await this.klarna_.get(
|
`${this.klarnaOrderUrl_}/${order_id}`
|
||||||
`${this.klarnaOrderUrl_}/${order_id}`
|
)
|
||||||
)
|
|
||||||
|
|
||||||
let status = "initial"
|
let status = "pending"
|
||||||
if (order.status === "checkout_complete") {
|
|
||||||
status = "authorized"
|
if (order.status === "checkout_complete") {
|
||||||
}
|
status = "authorized"
|
||||||
return status
|
|
||||||
} catch (error) {
|
|
||||||
throw error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return status
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -249,9 +259,12 @@ class KlarnaProviderService extends PaymentService {
|
|||||||
async createPayment(cart) {
|
async createPayment(cart) {
|
||||||
try {
|
try {
|
||||||
const order = await this.cartToKlarnaOrder(cart)
|
const order = await this.cartToKlarnaOrder(cart)
|
||||||
return this.klarna_
|
|
||||||
|
const klarnaPayment = await this.klarna_
|
||||||
.post(this.klarnaOrderUrl_, order)
|
.post(this.klarnaOrderUrl_, order)
|
||||||
.then(({ data }) => data)
|
.then(({ data }) => data)
|
||||||
|
|
||||||
|
return klarnaPayment
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
@@ -272,6 +285,21 @@ class KlarnaProviderService extends PaymentService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a Klarna payment objec.
|
||||||
|
* @param {object} sessionData - the data of the payment to retrieve
|
||||||
|
* @returns {Promise<object>} Stripe payment intent
|
||||||
|
*/
|
||||||
|
async getPaymentData(sessionData) {
|
||||||
|
try {
|
||||||
|
return this.klarna_
|
||||||
|
.get(`${this.klarnaOrderUrl_}/${sessionData.data.order_id}`)
|
||||||
|
.then(({ data }) => data)
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves completed Klarna Order.
|
* Retrieves completed Klarna Order.
|
||||||
* @param {string} klarnaOrderId - id of the order to retrieve
|
* @param {string} klarnaOrderId - id of the order to retrieve
|
||||||
@@ -287,6 +315,23 @@ class KlarnaProviderService extends PaymentService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authorizes Klarna payment by simply returning the status for the payment
|
||||||
|
* in use.
|
||||||
|
* @param {object} sessionData - payment session data
|
||||||
|
* @param {object} context - properties relevant to current context
|
||||||
|
* @returns {Promise<{ status: string, data: object }>} result with data and status
|
||||||
|
*/
|
||||||
|
async authorizePayment(sessionData, context = {}) {
|
||||||
|
try {
|
||||||
|
const paymentStatus = await this.getStatus(sessionData.data)
|
||||||
|
|
||||||
|
return { data: sessionData.data, status: paymentStatus }
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Acknowledges a Klarna order as part of the order completion process
|
* Acknowledges a Klarna order as part of the order completion process
|
||||||
* @param {string} klarnaOrderId - id of the order to acknowledge
|
* @param {string} klarnaOrderId - id of the order to acknowledge
|
||||||
@@ -332,6 +377,14 @@ class KlarnaProviderService extends PaymentService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updatePaymentData(sessionData, update) {
|
||||||
|
try {
|
||||||
|
return { ...sessionData, ...update }
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates Klarna order.
|
* Updates Klarna order.
|
||||||
* @param {string} order - the order to update
|
* @param {string} order - the order to update
|
||||||
@@ -339,14 +392,14 @@ class KlarnaProviderService extends PaymentService {
|
|||||||
* @returns {Object} updated order
|
* @returns {Object} updated order
|
||||||
*/
|
*/
|
||||||
async updatePayment(paymentData, cart) {
|
async updatePayment(paymentData, cart) {
|
||||||
try {
|
if (cart.total !== paymentData.order_amount) {
|
||||||
const order = await this.cartToKlarnaOrder(cart, true)
|
const order = await this.cartToKlarnaOrder(cart)
|
||||||
return this.klarna_
|
return this.klarna_
|
||||||
.post(`${this.klarnaOrderUrl_}/${paymentData.order_id}`, order)
|
.post(`${this.klarnaOrderUrl_}/${paymentData.order_id}`, order)
|
||||||
.then(({ data }) => data)
|
.then(({ data }) => data)
|
||||||
} catch (error) {
|
|
||||||
throw error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return paymentData
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -354,9 +407,9 @@ class KlarnaProviderService extends PaymentService {
|
|||||||
* @param {Object} paymentData - payment method data from cart
|
* @param {Object} paymentData - payment method data from cart
|
||||||
* @returns {string} id of captured order
|
* @returns {string} id of captured order
|
||||||
*/
|
*/
|
||||||
async capturePayment(paymentData) {
|
async capturePayment(payment) {
|
||||||
|
const { order_id } = payment.data
|
||||||
try {
|
try {
|
||||||
const { order_id } = paymentData
|
|
||||||
const { data: order } = await this.klarna_.get(
|
const { data: order } = await this.klarna_.get(
|
||||||
`${this.klarnaOrderManagementUrl_}/${order_id}`
|
`${this.klarnaOrderManagementUrl_}/${order_id}`
|
||||||
)
|
)
|
||||||
@@ -368,7 +421,8 @@ class KlarnaProviderService extends PaymentService {
|
|||||||
captured_amount: order_amount,
|
captured_amount: order_amount,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return order_id
|
|
||||||
|
return this.retrieveCompletedOrder(order_id)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
@@ -379,16 +433,17 @@ class KlarnaProviderService extends PaymentService {
|
|||||||
* @param {Object} paymentData - payment method data from cart
|
* @param {Object} paymentData - payment method data from cart
|
||||||
* @returns {string} id of refunded order
|
* @returns {string} id of refunded order
|
||||||
*/
|
*/
|
||||||
async refundPayment(paymentData, amount) {
|
async refundPayment(payment, amountToRefund) {
|
||||||
|
const { order_id } = payment.data
|
||||||
try {
|
try {
|
||||||
const { order_id } = paymentData
|
|
||||||
await this.klarna_.post(
|
await this.klarna_.post(
|
||||||
`${this.klarnaOrderManagementUrl_}/${order_id}/refunds`,
|
`${this.klarnaOrderManagementUrl_}/${order_id}/refunds`,
|
||||||
{
|
{
|
||||||
refunded_amount: amount * 100,
|
refunded_amount: amountToRefund,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return order_id
|
|
||||||
|
return this.retrieveCompletedOrder(order_id)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
@@ -399,17 +454,22 @@ class KlarnaProviderService extends PaymentService {
|
|||||||
* @param {Object} paymentData - payment method data from cart
|
* @param {Object} paymentData - payment method data from cart
|
||||||
* @returns {string} id of cancelled order
|
* @returns {string} id of cancelled order
|
||||||
*/
|
*/
|
||||||
async cancelPayment(paymentData) {
|
async cancelPayment(payment) {
|
||||||
|
const { order_id } = payment.data
|
||||||
try {
|
try {
|
||||||
const { order_id } = paymentData
|
|
||||||
await this.klarna_.post(
|
await this.klarna_.post(
|
||||||
`${this.klarnaOrderManagementUrl_}/${order_id}/cancel`
|
`${this.klarnaOrderManagementUrl_}/${order_id}/cancel`
|
||||||
)
|
)
|
||||||
return order_id
|
|
||||||
|
return this.retrieveCompletedOrder(order_id)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async deletePayment(_) {
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default KlarnaProviderService
|
export default KlarnaProviderService
|
||||||
@@ -3595,7 +3595,7 @@ joi-objectid@^3.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/joi-objectid/-/joi-objectid-3.0.1.tgz#63ace7860f8e1a993a28d40c40ffd8eff01a3668"
|
resolved "https://registry.yarnpkg.com/joi-objectid/-/joi-objectid-3.0.1.tgz#63ace7860f8e1a993a28d40c40ffd8eff01a3668"
|
||||||
integrity sha512-V/3hbTlGpvJ03Me6DJbdBI08hBTasFOmipsauOsxOSnsF1blxV537WTl1zPwbfcKle4AK0Ma4OPnzMH4LlvTpQ==
|
integrity sha512-V/3hbTlGpvJ03Me6DJbdBI08hBTasFOmipsauOsxOSnsF1blxV537WTl1zPwbfcKle4AK0Ma4OPnzMH4LlvTpQ==
|
||||||
|
|
||||||
joi@^17.2.1:
|
joi@^17.2.1, joi@^17.3.0:
|
||||||
version "17.3.0"
|
version "17.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/joi/-/joi-17.3.0.tgz#f1be4a6ce29bc1716665819ac361dfa139fff5d2"
|
resolved "https://registry.yarnpkg.com/joi/-/joi-17.3.0.tgz#f1be4a6ce29bc1716665819ac361dfa139fff5d2"
|
||||||
integrity sha512-Qh5gdU6niuYbUIUV5ejbsMiiFmBdw8Kcp8Buj2JntszCkCfxJ9Cz76OtHxOZMPXrt5810iDIXs+n1nNVoquHgg==
|
integrity sha512-Qh5gdU6niuYbUIUV5ejbsMiiFmBdw8Kcp8Buj2JntszCkCfxJ9Cz76OtHxOZMPXrt5810iDIXs+n1nNVoquHgg==
|
||||||
@@ -3857,21 +3857,29 @@ media-typer@0.3.0:
|
|||||||
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
||||||
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
|
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
|
||||||
|
|
||||||
medusa-core-utils@^1.0.10:
|
medusa-core-utils@^1.0.11:
|
||||||
version "1.0.10"
|
version "1.0.11"
|
||||||
resolved "https://registry.yarnpkg.com/medusa-core-utils/-/medusa-core-utils-1.0.10.tgz#6175f1e428318205742621f78d387254fc85b8a1"
|
resolved "https://registry.yarnpkg.com/medusa-core-utils/-/medusa-core-utils-1.0.11.tgz#c77685c4b637f6aebe808e82dc4f7739c2f9ff79"
|
||||||
integrity sha512-z12ITzPl5UpDJTILNXcMGD4yujSRkuvVtxkrvqCmA44IEyRj1hWZ0dGbVzuHj2wIahDHFFg66oq91IQH5QVsyg==
|
integrity sha512-Nnp7RCWsR4MJNrslC0YB/wMO1Wf2QJH0aras+tjRdymXfR/ySn2/0+v8pyXxhY8XXYzOZknnjdCB8KI9+PHlsA==
|
||||||
dependencies:
|
dependencies:
|
||||||
joi "^17.2.1"
|
joi "^17.2.1"
|
||||||
joi-objectid "^3.0.1"
|
joi-objectid "^3.0.1"
|
||||||
|
|
||||||
medusa-test-utils@^1.0.11:
|
medusa-core-utils@^1.0.12-alpha.787+0646bd3:
|
||||||
version "1.0.11"
|
version "1.0.12-alpha.801"
|
||||||
resolved "https://registry.yarnpkg.com/medusa-test-utils/-/medusa-test-utils-1.0.11.tgz#bae901efa90426fb64818de700dc6e163820160f"
|
resolved "https://registry.yarnpkg.com/medusa-core-utils/-/medusa-core-utils-1.0.12-alpha.801.tgz#0a7d0b5d4b2290d9a2f7afb35ab1f0bec666dbb0"
|
||||||
integrity sha512-CSNb70sXOfKTndzxWtPMYq+KeYaFkSLAPUyhuwDNVvkbne0faOYF5OMCV8r3aLoQ3D1Tvjrb/XB9Qt17ycjuPw==
|
integrity sha512-1ppxgl8noZfO3JLLHeLj4eEMOwTI/LcCxWwwD7AUp5uP1brPmctthDTM/pOMrEuytKWIqaNXCRohvAlgDr0vYA==
|
||||||
|
dependencies:
|
||||||
|
joi "^17.3.0"
|
||||||
|
joi-objectid "^3.0.1"
|
||||||
|
|
||||||
|
medusa-test-utils@^1.0.12-alpha.176+0646bd3:
|
||||||
|
version "1.0.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/medusa-test-utils/-/medusa-test-utils-1.0.13.tgz#f728b8dacb1ba64d85529e3f2e9dba6c7e315416"
|
||||||
|
integrity sha512-EBbjGLHFEG/Fnqneire/bNCPeseo1ZFF3n3yMUfp0JDmBAVgE8L52wJa8Zeu36TL/rbQsQAsTAS6La46A8IJgg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/plugin-transform-classes" "^7.9.5"
|
"@babel/plugin-transform-classes" "^7.9.5"
|
||||||
medusa-core-utils "^1.0.10"
|
medusa-core-utils "^1.0.11"
|
||||||
mongoose "^5.8.0"
|
mongoose "^5.8.0"
|
||||||
|
|
||||||
memory-pager@^1.0.2:
|
memory-pager@^1.0.2:
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"plugins": [
|
"plugins": [
|
||||||
|
"@babel/plugin-proposal-optional-chaining",
|
||||||
"@babel/plugin-proposal-class-properties",
|
"@babel/plugin-proposal-class-properties",
|
||||||
"@babel/plugin-transform-instanceof",
|
"@babel/plugin-transform-instanceof",
|
||||||
"@babel/plugin-transform-classes"
|
"@babel/plugin-transform-classes"
|
||||||
|
|||||||
@@ -13,4 +13,3 @@ yarn.lock
|
|||||||
/models
|
/models
|
||||||
/subscribers
|
/subscribers
|
||||||
/__mocks__
|
/__mocks__
|
||||||
|
|
||||||
@@ -5,5 +5,9 @@ node_modules
|
|||||||
/*.js
|
/*.js
|
||||||
!index.js
|
!index.js
|
||||||
yarn.lock
|
yarn.lock
|
||||||
|
src
|
||||||
|
.gitignore
|
||||||
|
.eslintrc
|
||||||
|
.babelrc
|
||||||
|
.prettierrc
|
||||||
|
|
||||||
@@ -7,13 +7,17 @@ exports["default"] = exports.CartServiceMock = exports.carts = void 0;
|
|||||||
|
|
||||||
var _medusaTestUtils = require("medusa-test-utils");
|
var _medusaTestUtils = require("medusa-test-utils");
|
||||||
|
|
||||||
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||||
|
|
||||||
var carts = {
|
var carts = {
|
||||||
emptyCart: {
|
emptyCart: {
|
||||||
_id: _medusaTestUtils.IdMap.getId("emptyCart"),
|
id: _medusaTestUtils.IdMap.getId("emptyCart"),
|
||||||
items: [],
|
items: [],
|
||||||
region_id: _medusaTestUtils.IdMap.getId("testRegion"),
|
region_id: _medusaTestUtils.IdMap.getId("testRegion"),
|
||||||
|
customer_id: "test-customer",
|
||||||
|
payment_sessions: [],
|
||||||
shipping_options: [{
|
shipping_options: [{
|
||||||
_id: _medusaTestUtils.IdMap.getId("freeShipping"),
|
id: _medusaTestUtils.IdMap.getId("freeShipping"),
|
||||||
profile_id: "default_profile",
|
profile_id: "default_profile",
|
||||||
data: {
|
data: {
|
||||||
some_data: "yes"
|
some_data: "yes"
|
||||||
@@ -21,58 +25,53 @@ var carts = {
|
|||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
frCart: {
|
frCart: {
|
||||||
_id: _medusaTestUtils.IdMap.getId("fr-cart"),
|
id: _medusaTestUtils.IdMap.getId("fr-cart"),
|
||||||
email: "lebron@james.com",
|
email: "lebron@james.com",
|
||||||
title: "test",
|
title: "test",
|
||||||
region_id: _medusaTestUtils.IdMap.getId("region-france"),
|
region_id: _medusaTestUtils.IdMap.getId("region-france"),
|
||||||
items: [{
|
items: [{
|
||||||
_id: _medusaTestUtils.IdMap.getId("line"),
|
id: _medusaTestUtils.IdMap.getId("line"),
|
||||||
title: "merge line",
|
title: "merge line",
|
||||||
description: "This is a new line",
|
description: "This is a new line",
|
||||||
thumbnail: "test-img-yeah.com/thumb",
|
thumbnail: "test-img-yeah.com/thumb",
|
||||||
content: [{
|
unit_price: 8,
|
||||||
unit_price: 8,
|
variant: {
|
||||||
variant: {
|
id: _medusaTestUtils.IdMap.getId("eur-8-us-10")
|
||||||
_id: _medusaTestUtils.IdMap.getId("eur-8-us-10")
|
|
||||||
},
|
|
||||||
product: {
|
|
||||||
_id: _medusaTestUtils.IdMap.getId("product")
|
|
||||||
},
|
|
||||||
quantity: 1
|
|
||||||
}, {
|
|
||||||
unit_price: 10,
|
|
||||||
variant: {
|
|
||||||
_id: _medusaTestUtils.IdMap.getId("eur-10-us-12")
|
|
||||||
},
|
|
||||||
product: {
|
|
||||||
_id: _medusaTestUtils.IdMap.getId("product")
|
|
||||||
},
|
|
||||||
quantity: 1
|
|
||||||
}],
|
|
||||||
quantity: 10
|
|
||||||
}, {
|
|
||||||
_id: _medusaTestUtils.IdMap.getId("existingLine"),
|
|
||||||
title: "merge line",
|
|
||||||
description: "This is a new line",
|
|
||||||
thumbnail: "test-img-yeah.com/thumb",
|
|
||||||
content: {
|
|
||||||
unit_price: 10,
|
|
||||||
variant: {
|
|
||||||
_id: _medusaTestUtils.IdMap.getId("eur-10-us-12")
|
|
||||||
},
|
|
||||||
product: {
|
|
||||||
_id: _medusaTestUtils.IdMap.getId("product")
|
|
||||||
},
|
|
||||||
quantity: 1
|
|
||||||
},
|
},
|
||||||
|
product: {
|
||||||
|
id: _medusaTestUtils.IdMap.getId("product")
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// unit_price: 10,
|
||||||
|
// variant: {
|
||||||
|
// id: IdMap.getId("eur-10-us-12"),
|
||||||
|
// },
|
||||||
|
// product: {
|
||||||
|
// id: IdMap.getId("product"),
|
||||||
|
// },
|
||||||
|
// quantity: 1,
|
||||||
|
// },
|
||||||
quantity: 10
|
quantity: 10
|
||||||
}],
|
}, _defineProperty({
|
||||||
|
id: _medusaTestUtils.IdMap.getId("existingLine"),
|
||||||
|
title: "merge line",
|
||||||
|
description: "This is a new line",
|
||||||
|
thumbnail: "test-img-yeah.com/thumb",
|
||||||
|
unit_price: 10,
|
||||||
|
variant: {
|
||||||
|
id: _medusaTestUtils.IdMap.getId("eur-10-us-12")
|
||||||
|
},
|
||||||
|
product: {
|
||||||
|
id: _medusaTestUtils.IdMap.getId("product")
|
||||||
|
},
|
||||||
|
quantity: 1
|
||||||
|
}, "quantity", 10)],
|
||||||
shipping_methods: [{
|
shipping_methods: [{
|
||||||
_id: _medusaTestUtils.IdMap.getId("freeShipping"),
|
id: _medusaTestUtils.IdMap.getId("freeShipping"),
|
||||||
profile_id: "default_profile"
|
profile_id: "default_profile"
|
||||||
}],
|
}],
|
||||||
shipping_options: [{
|
shipping_options: [{
|
||||||
_id: _medusaTestUtils.IdMap.getId("freeShipping"),
|
id: _medusaTestUtils.IdMap.getId("freeShipping"),
|
||||||
profile_id: "default_profile"
|
profile_id: "default_profile"
|
||||||
}],
|
}],
|
||||||
payment_sessions: [{
|
payment_sessions: [{
|
||||||
@@ -95,57 +94,57 @@ var carts = {
|
|||||||
customer_id: _medusaTestUtils.IdMap.getId("lebron")
|
customer_id: _medusaTestUtils.IdMap.getId("lebron")
|
||||||
},
|
},
|
||||||
frCartNoStripeCustomer: {
|
frCartNoStripeCustomer: {
|
||||||
_id: _medusaTestUtils.IdMap.getId("fr-cart-no-customer"),
|
id: _medusaTestUtils.IdMap.getId("fr-cart-no-customer"),
|
||||||
title: "test",
|
title: "test",
|
||||||
region_id: _medusaTestUtils.IdMap.getId("region-france"),
|
region_id: _medusaTestUtils.IdMap.getId("region-france"),
|
||||||
items: [{
|
items: [{
|
||||||
_id: _medusaTestUtils.IdMap.getId("line"),
|
id: _medusaTestUtils.IdMap.getId("line"),
|
||||||
title: "merge line",
|
title: "merge line",
|
||||||
description: "This is a new line",
|
description: "This is a new line",
|
||||||
thumbnail: "test-img-yeah.com/thumb",
|
thumbnail: "test-img-yeah.com/thumb",
|
||||||
content: [{
|
content: [{
|
||||||
unit_price: 8,
|
unit_price: 8,
|
||||||
variant: {
|
variant: {
|
||||||
_id: _medusaTestUtils.IdMap.getId("eur-8-us-10")
|
id: _medusaTestUtils.IdMap.getId("eur-8-us-10")
|
||||||
},
|
},
|
||||||
product: {
|
product: {
|
||||||
_id: _medusaTestUtils.IdMap.getId("product")
|
id: _medusaTestUtils.IdMap.getId("product")
|
||||||
},
|
},
|
||||||
quantity: 1
|
quantity: 1
|
||||||
}, {
|
}, {
|
||||||
unit_price: 10,
|
unit_price: 10,
|
||||||
variant: {
|
variant: {
|
||||||
_id: _medusaTestUtils.IdMap.getId("eur-10-us-12")
|
id: _medusaTestUtils.IdMap.getId("eur-10-us-12")
|
||||||
},
|
},
|
||||||
product: {
|
product: {
|
||||||
_id: _medusaTestUtils.IdMap.getId("product")
|
id: _medusaTestUtils.IdMap.getId("product")
|
||||||
},
|
},
|
||||||
quantity: 1
|
quantity: 1
|
||||||
}],
|
}],
|
||||||
quantity: 10
|
quantity: 10
|
||||||
}, {
|
}, {
|
||||||
_id: _medusaTestUtils.IdMap.getId("existingLine"),
|
id: _medusaTestUtils.IdMap.getId("existingLine"),
|
||||||
title: "merge line",
|
title: "merge line",
|
||||||
description: "This is a new line",
|
description: "This is a new line",
|
||||||
thumbnail: "test-img-yeah.com/thumb",
|
thumbnail: "test-img-yeah.com/thumb",
|
||||||
content: {
|
content: {
|
||||||
unit_price: 10,
|
unit_price: 10,
|
||||||
variant: {
|
variant: {
|
||||||
_id: _medusaTestUtils.IdMap.getId("eur-10-us-12")
|
id: _medusaTestUtils.IdMap.getId("eur-10-us-12")
|
||||||
},
|
},
|
||||||
product: {
|
product: {
|
||||||
_id: _medusaTestUtils.IdMap.getId("product")
|
id: _medusaTestUtils.IdMap.getId("product")
|
||||||
},
|
},
|
||||||
quantity: 1
|
quantity: 1
|
||||||
},
|
},
|
||||||
quantity: 10
|
quantity: 10
|
||||||
}],
|
}],
|
||||||
shipping_methods: [{
|
shipping_methods: [{
|
||||||
_id: _medusaTestUtils.IdMap.getId("freeShipping"),
|
id: _medusaTestUtils.IdMap.getId("freeShipping"),
|
||||||
profile_id: "default_profile"
|
profile_id: "default_profile"
|
||||||
}],
|
}],
|
||||||
shipping_options: [{
|
shipping_options: [{
|
||||||
_id: _medusaTestUtils.IdMap.getId("freeShipping"),
|
id: _medusaTestUtils.IdMap.getId("freeShipping"),
|
||||||
profile_id: "default_profile"
|
profile_id: "default_profile"
|
||||||
}],
|
}],
|
||||||
payment_sessions: [{
|
payment_sessions: [{
|
||||||
@@ -175,6 +174,10 @@ var CartServiceMock = {
|
|||||||
return Promise.resolve(carts.frCart);
|
return Promise.resolve(carts.frCart);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cartId === _medusaTestUtils.IdMap.getId("fr-cart-no-customer")) {
|
||||||
|
return Promise.resolve(carts.frCartNoStripeCustomer);
|
||||||
|
}
|
||||||
|
|
||||||
if (cartId === _medusaTestUtils.IdMap.getId("emptyCart")) {
|
if (cartId === _medusaTestUtils.IdMap.getId("emptyCart")) {
|
||||||
return Promise.resolve(carts.emptyCart);
|
return Promise.resolve(carts.emptyCart);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-payment-stripe",
|
"name": "medusa-payment-stripe",
|
||||||
"version": "1.0.15",
|
"version": "1.0.14-alpha.176+0646bd3",
|
||||||
"description": "Stripe Payment provider for Meduas Commerce",
|
"description": "Stripe Payment provider for Meduas Commerce",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
"@babel/core": "^7.7.5",
|
"@babel/core": "^7.7.5",
|
||||||
"@babel/node": "^7.7.4",
|
"@babel/node": "^7.7.4",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.7.4",
|
"@babel/plugin-proposal-class-properties": "^7.7.4",
|
||||||
|
"@babel/plugin-proposal-optional-chaining": "^7.12.7",
|
||||||
"@babel/plugin-transform-classes": "^7.9.5",
|
"@babel/plugin-transform-classes": "^7.9.5",
|
||||||
"@babel/plugin-transform-instanceof": "^7.8.3",
|
"@babel/plugin-transform-instanceof": "^7.8.3",
|
||||||
"@babel/plugin-transform-runtime": "^7.7.6",
|
"@babel/plugin-transform-runtime": "^7.7.6",
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
"cross-env": "^5.2.1",
|
"cross-env": "^5.2.1",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"jest": "^25.5.2",
|
"jest": "^25.5.2",
|
||||||
"medusa-test-utils": "^1.0.13"
|
"medusa-test-utils": "^1.0.12-alpha.176+0646bd3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "babel src -d . --ignore **/__tests__",
|
"build": "babel src -d . --ignore **/__tests__",
|
||||||
@@ -39,8 +40,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"medusa-core-utils": "^1.0.11",
|
"medusa-core-utils": "^1.0.12-alpha.787+0646bd3",
|
||||||
"stripe": "^8.50.0"
|
"stripe": "^8.50.0"
|
||||||
},
|
},
|
||||||
"gitHead": "3cc7cbe5124cbcbb75f6e1435db4dcfaa2a60408"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -2,12 +2,14 @@ import { IdMap } from "medusa-test-utils"
|
|||||||
|
|
||||||
export const carts = {
|
export const carts = {
|
||||||
emptyCart: {
|
emptyCart: {
|
||||||
_id: IdMap.getId("emptyCart"),
|
id: IdMap.getId("emptyCart"),
|
||||||
items: [],
|
items: [],
|
||||||
region_id: IdMap.getId("testRegion"),
|
region_id: IdMap.getId("testRegion"),
|
||||||
|
customer_id: "test-customer",
|
||||||
|
payment_sessions: [],
|
||||||
shipping_options: [
|
shipping_options: [
|
||||||
{
|
{
|
||||||
_id: IdMap.getId("freeShipping"),
|
id: IdMap.getId("freeShipping"),
|
||||||
profile_id: "default_profile",
|
profile_id: "default_profile",
|
||||||
data: {
|
data: {
|
||||||
some_data: "yes",
|
some_data: "yes",
|
||||||
@@ -16,67 +18,61 @@ export const carts = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
frCart: {
|
frCart: {
|
||||||
_id: IdMap.getId("fr-cart"),
|
id: IdMap.getId("fr-cart"),
|
||||||
email: "lebron@james.com",
|
email: "lebron@james.com",
|
||||||
title: "test",
|
title: "test",
|
||||||
region_id: IdMap.getId("region-france"),
|
region_id: IdMap.getId("region-france"),
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
_id: IdMap.getId("line"),
|
id: IdMap.getId("line"),
|
||||||
title: "merge line",
|
title: "merge line",
|
||||||
description: "This is a new line",
|
description: "This is a new line",
|
||||||
thumbnail: "test-img-yeah.com/thumb",
|
thumbnail: "test-img-yeah.com/thumb",
|
||||||
content: [
|
|
||||||
{
|
unit_price: 8,
|
||||||
unit_price: 8,
|
variant: {
|
||||||
variant: {
|
id: IdMap.getId("eur-8-us-10"),
|
||||||
_id: IdMap.getId("eur-8-us-10"),
|
},
|
||||||
},
|
product: {
|
||||||
product: {
|
id: IdMap.getId("product"),
|
||||||
_id: IdMap.getId("product"),
|
},
|
||||||
},
|
// {
|
||||||
quantity: 1,
|
// unit_price: 10,
|
||||||
},
|
// variant: {
|
||||||
{
|
// id: IdMap.getId("eur-10-us-12"),
|
||||||
unit_price: 10,
|
// },
|
||||||
variant: {
|
// product: {
|
||||||
_id: IdMap.getId("eur-10-us-12"),
|
// id: IdMap.getId("product"),
|
||||||
},
|
// },
|
||||||
product: {
|
// quantity: 1,
|
||||||
_id: IdMap.getId("product"),
|
// },
|
||||||
},
|
|
||||||
quantity: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
quantity: 10,
|
quantity: 10,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
_id: IdMap.getId("existingLine"),
|
id: IdMap.getId("existingLine"),
|
||||||
title: "merge line",
|
title: "merge line",
|
||||||
description: "This is a new line",
|
description: "This is a new line",
|
||||||
thumbnail: "test-img-yeah.com/thumb",
|
thumbnail: "test-img-yeah.com/thumb",
|
||||||
content: {
|
unit_price: 10,
|
||||||
unit_price: 10,
|
variant: {
|
||||||
variant: {
|
id: IdMap.getId("eur-10-us-12"),
|
||||||
_id: IdMap.getId("eur-10-us-12"),
|
|
||||||
},
|
|
||||||
product: {
|
|
||||||
_id: IdMap.getId("product"),
|
|
||||||
},
|
|
||||||
quantity: 1,
|
|
||||||
},
|
},
|
||||||
|
product: {
|
||||||
|
id: IdMap.getId("product"),
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
quantity: 10,
|
quantity: 10,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
shipping_methods: [
|
shipping_methods: [
|
||||||
{
|
{
|
||||||
_id: IdMap.getId("freeShipping"),
|
id: IdMap.getId("freeShipping"),
|
||||||
profile_id: "default_profile",
|
profile_id: "default_profile",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
shipping_options: [
|
shipping_options: [
|
||||||
{
|
{
|
||||||
_id: IdMap.getId("freeShipping"),
|
id: IdMap.getId("freeShipping"),
|
||||||
profile_id: "default_profile",
|
profile_id: "default_profile",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -102,12 +98,12 @@ export const carts = {
|
|||||||
customer_id: IdMap.getId("lebron"),
|
customer_id: IdMap.getId("lebron"),
|
||||||
},
|
},
|
||||||
frCartNoStripeCustomer: {
|
frCartNoStripeCustomer: {
|
||||||
_id: IdMap.getId("fr-cart-no-customer"),
|
id: IdMap.getId("fr-cart-no-customer"),
|
||||||
title: "test",
|
title: "test",
|
||||||
region_id: IdMap.getId("region-france"),
|
region_id: IdMap.getId("region-france"),
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
_id: IdMap.getId("line"),
|
id: IdMap.getId("line"),
|
||||||
title: "merge line",
|
title: "merge line",
|
||||||
description: "This is a new line",
|
description: "This is a new line",
|
||||||
thumbnail: "test-img-yeah.com/thumb",
|
thumbnail: "test-img-yeah.com/thumb",
|
||||||
@@ -115,20 +111,20 @@ export const carts = {
|
|||||||
{
|
{
|
||||||
unit_price: 8,
|
unit_price: 8,
|
||||||
variant: {
|
variant: {
|
||||||
_id: IdMap.getId("eur-8-us-10"),
|
id: IdMap.getId("eur-8-us-10"),
|
||||||
},
|
},
|
||||||
product: {
|
product: {
|
||||||
_id: IdMap.getId("product"),
|
id: IdMap.getId("product"),
|
||||||
},
|
},
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
unit_price: 10,
|
unit_price: 10,
|
||||||
variant: {
|
variant: {
|
||||||
_id: IdMap.getId("eur-10-us-12"),
|
id: IdMap.getId("eur-10-us-12"),
|
||||||
},
|
},
|
||||||
product: {
|
product: {
|
||||||
_id: IdMap.getId("product"),
|
id: IdMap.getId("product"),
|
||||||
},
|
},
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
},
|
},
|
||||||
@@ -136,17 +132,17 @@ export const carts = {
|
|||||||
quantity: 10,
|
quantity: 10,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
_id: IdMap.getId("existingLine"),
|
id: IdMap.getId("existingLine"),
|
||||||
title: "merge line",
|
title: "merge line",
|
||||||
description: "This is a new line",
|
description: "This is a new line",
|
||||||
thumbnail: "test-img-yeah.com/thumb",
|
thumbnail: "test-img-yeah.com/thumb",
|
||||||
content: {
|
content: {
|
||||||
unit_price: 10,
|
unit_price: 10,
|
||||||
variant: {
|
variant: {
|
||||||
_id: IdMap.getId("eur-10-us-12"),
|
id: IdMap.getId("eur-10-us-12"),
|
||||||
},
|
},
|
||||||
product: {
|
product: {
|
||||||
_id: IdMap.getId("product"),
|
id: IdMap.getId("product"),
|
||||||
},
|
},
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
},
|
},
|
||||||
@@ -155,13 +151,13 @@ export const carts = {
|
|||||||
],
|
],
|
||||||
shipping_methods: [
|
shipping_methods: [
|
||||||
{
|
{
|
||||||
_id: IdMap.getId("freeShipping"),
|
id: IdMap.getId("freeShipping"),
|
||||||
profile_id: "default_profile",
|
profile_id: "default_profile",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
shipping_options: [
|
shipping_options: [
|
||||||
{
|
{
|
||||||
_id: IdMap.getId("freeShipping"),
|
id: IdMap.getId("freeShipping"),
|
||||||
profile_id: "default_profile",
|
profile_id: "default_profile",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -193,6 +189,9 @@ export const CartServiceMock = {
|
|||||||
if (cartId === IdMap.getId("fr-cart")) {
|
if (cartId === IdMap.getId("fr-cart")) {
|
||||||
return Promise.resolve(carts.frCart)
|
return Promise.resolve(carts.frCart)
|
||||||
}
|
}
|
||||||
|
if (cartId === IdMap.getId("fr-cart-no-customer")) {
|
||||||
|
return Promise.resolve(carts.frCartNoStripeCustomer)
|
||||||
|
}
|
||||||
if (cartId === IdMap.getId("emptyCart")) {
|
if (cartId === IdMap.getId("emptyCart")) {
|
||||||
return Promise.resolve(carts.emptyCart)
|
return Promise.resolve(carts.emptyCart)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export default async (req, res) => {
|
|||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case "payment_intent.succeeded":
|
case "payment_intent.succeeded":
|
||||||
if (order) {
|
if (order) {
|
||||||
await orderService.update(order._id, {
|
await orderService.update(order.id, {
|
||||||
payment_status: "captured",
|
payment_status: "captured",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export const StripeProviderServiceMock = {
|
|||||||
}
|
}
|
||||||
if (payData.id === "pi_no") {
|
if (payData.id === "pi_no") {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
id: "pi",
|
id: "pi_no",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return Promise.resolve(undefined)
|
return Promise.resolve(undefined)
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ describe("StripeProviderService", () => {
|
|||||||
result = await stripeProviderService.updatePayment(
|
result = await stripeProviderService.updatePayment(
|
||||||
{
|
{
|
||||||
id: "pi_lebron",
|
id: "pi_lebron",
|
||||||
|
amount: 800,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
total: 1000,
|
total: 1000,
|
||||||
@@ -136,7 +137,7 @@ describe("StripeProviderService", () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("returns cancelled stripe payment intent", () => {
|
it("returns updated stripe payment intent", () => {
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
id: "pi_lebron",
|
id: "pi_lebron",
|
||||||
customer: "cus_lebron",
|
customer: "cus_lebron",
|
||||||
@@ -186,7 +187,13 @@ describe("StripeProviderService", () => {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
result = await stripeProviderService.capturePayment("pi_lebron")
|
result = await stripeProviderService.capturePayment({
|
||||||
|
data: {
|
||||||
|
id: "pi_lebron",
|
||||||
|
customer: "cus_lebron",
|
||||||
|
amount: 1000,
|
||||||
|
},
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("returns captured stripe payment intent", () => {
|
it("returns captured stripe payment intent", () => {
|
||||||
@@ -210,7 +217,17 @@ describe("StripeProviderService", () => {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
result = await stripeProviderService.refundPayment("pi_lebron", 1000)
|
result = await stripeProviderService.refundPayment(
|
||||||
|
{
|
||||||
|
data: {
|
||||||
|
id: "re_123",
|
||||||
|
payment_intent: "pi_lebron",
|
||||||
|
amount: 1000,
|
||||||
|
status: "succeeded",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
1000
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("returns refunded stripe payment intent", () => {
|
it("returns refunded stripe payment intent", () => {
|
||||||
@@ -234,7 +251,13 @@ describe("StripeProviderService", () => {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
result = await stripeProviderService.cancelPayment("pi_lebron")
|
result = await stripeProviderService.cancelPayment({
|
||||||
|
data: {
|
||||||
|
id: "pi_lebron",
|
||||||
|
customer: "cus_lebron",
|
||||||
|
status: "cancelled",
|
||||||
|
},
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("returns cancelled stripe payment intent", () => {
|
it("returns cancelled stripe payment intent", () => {
|
||||||
|
|||||||
@@ -8,48 +8,78 @@ class StripeProviderService extends PaymentService {
|
|||||||
constructor({ customerService, totalsService, regionService }, options) {
|
constructor({ customerService, totalsService, regionService }, options) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Required Stripe options:
|
||||||
|
* {
|
||||||
|
* api_key: "stripe_secret_key", REQUIRED
|
||||||
|
* webhook_secret: "stripe_webhook_secret", REQUIRED
|
||||||
|
* // Use this flag to capture payment immediately (default is false)
|
||||||
|
* capture: true
|
||||||
|
* }
|
||||||
|
*/
|
||||||
this.options_ = options
|
this.options_ = options
|
||||||
|
|
||||||
|
/** @private @const {Stripe} */
|
||||||
this.stripe_ = Stripe(options.api_key)
|
this.stripe_ = Stripe(options.api_key)
|
||||||
|
|
||||||
|
/** @private @const {CustomerService} */
|
||||||
this.customerService_ = customerService
|
this.customerService_ = customerService
|
||||||
|
|
||||||
|
/** @private @const {RegionService} */
|
||||||
this.regionService_ = regionService
|
this.regionService_ = regionService
|
||||||
|
|
||||||
|
/** @private @const {TotalsService} */
|
||||||
this.totalsService_ = totalsService
|
this.totalsService_ = totalsService
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Status for Stripe PaymentIntent.
|
* Fetches Stripe payment intent. Check its status and returns the
|
||||||
* @param {Object} paymentData - payment method data from cart
|
* corresponding Medusa status.
|
||||||
|
* @param {object} paymentData - payment method data from cart
|
||||||
* @returns {string} the status of the payment intent
|
* @returns {string} the status of the payment intent
|
||||||
*/
|
*/
|
||||||
async getStatus(paymentData) {
|
async getStatus(paymentData) {
|
||||||
const { id } = paymentData
|
const { id } = paymentData
|
||||||
|
|
||||||
const paymentIntent = await this.stripe_.paymentIntents.retrieve(id)
|
const paymentIntent = await this.stripe_.paymentIntents.retrieve(id)
|
||||||
|
|
||||||
let status = "initial"
|
let status = "pending"
|
||||||
|
|
||||||
if (paymentIntent.status === "requires_payment_method") {
|
if (paymentIntent.status === "requires_payment_method") {
|
||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (paymentIntent.status === "requires_confirmation") {
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paymentIntent.status === "processing") {
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paymentIntent.status === "requires_action") {
|
||||||
|
status = "requires_more"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paymentIntent.status === "canceled") {
|
||||||
|
status = "canceled"
|
||||||
|
}
|
||||||
|
|
||||||
if (paymentIntent.status === "requires_capture") {
|
if (paymentIntent.status === "requires_capture") {
|
||||||
status = "authorized"
|
status = "authorized"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (paymentIntent.status === "succeeded") {
|
if (paymentIntent.status === "succeeded") {
|
||||||
status = "succeeded"
|
status = "authorized"
|
||||||
}
|
|
||||||
|
|
||||||
if (paymentIntent.status === "canceled") {
|
|
||||||
status = "canceled"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches a customers saved payment methods if registered in Stripe.
|
||||||
|
* @param {object} customer - customer to fetch saved cards for
|
||||||
|
* @returns {Promise<Array<object>>} saved payments methods
|
||||||
|
*/
|
||||||
async retrieveSavedMethods(customer) {
|
async retrieveSavedMethods(customer) {
|
||||||
if (customer.metadata && customer.metadata.stripe_id) {
|
if (customer.metadata && customer.metadata.stripe_id) {
|
||||||
const methods = await this.stripe_.paymentMethods.list({
|
const methods = await this.stripe_.paymentMethods.list({
|
||||||
@@ -63,6 +93,11 @@ class StripeProviderService extends PaymentService {
|
|||||||
return Promise.resolve([])
|
return Promise.resolve([])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches a Stripe customer
|
||||||
|
* @param {string} customerId - Stripe customer id
|
||||||
|
* @returns {Promise<object>} Stripe customer
|
||||||
|
*/
|
||||||
async retrieveCustomer(customerId) {
|
async retrieveCustomer(customerId) {
|
||||||
if (!customerId) {
|
if (!customerId) {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
@@ -70,17 +105,23 @@ class StripeProviderService extends PaymentService {
|
|||||||
return this.stripe_.customers.retrieve(customerId)
|
return this.stripe_.customers.retrieve(customerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// customer metadata
|
/**
|
||||||
|
* Creates a Stripe customer using a Medusa customer.
|
||||||
|
* @param {object} customer - Customer data from Medusa
|
||||||
|
* @returns {Promise<object>} Stripe customer
|
||||||
|
*/
|
||||||
async createCustomer(customer) {
|
async createCustomer(customer) {
|
||||||
try {
|
try {
|
||||||
const stripeCustomer = await this.stripe_.customers.create({
|
const stripeCustomer = await this.stripe_.customers.create({
|
||||||
email: customer.email,
|
email: customer.email,
|
||||||
})
|
})
|
||||||
await this.customerService_.setMetadata(
|
|
||||||
customer._id,
|
if (customer.id) {
|
||||||
"stripe_id",
|
await this.customerService_.update(customer.id, {
|
||||||
stripeCustomer.id
|
metadata: { stripe_id: stripeCustomer.id },
|
||||||
)
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return stripeCustomer
|
return stripeCustomer
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error
|
throw error
|
||||||
@@ -88,50 +129,57 @@ class StripeProviderService extends PaymentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates Stripe PaymentIntent.
|
* Creates a Stripe payment intent.
|
||||||
* @param {string} cart - the cart to create a payment for
|
* If customer is not registered in Stripe, we do so.
|
||||||
* @param {number} amount - the amount to create a payment for
|
* @param {object} cart - cart to create a payment for
|
||||||
* @returns {string} id of payment intent
|
* @returns {object} Stripe payment intent
|
||||||
*/
|
*/
|
||||||
async createPayment(cart) {
|
async createPayment(cart) {
|
||||||
const { customer_id, region_id } = cart
|
const { customer_id, region_id, email } = cart
|
||||||
const { currency_code } = await this.regionService_.retrieve(region_id)
|
const { currency_code } = await this.regionService_.retrieve(region_id)
|
||||||
|
|
||||||
let stripeCustomerId
|
|
||||||
if (!customer_id) {
|
|
||||||
const { id } = await this.stripe_.customers.create({
|
|
||||||
email: cart.email,
|
|
||||||
})
|
|
||||||
stripeCustomerId = id
|
|
||||||
} else {
|
|
||||||
const customer = await this.customerService_.retrieve(customer_id)
|
|
||||||
if (!(customer.metadata && customer.metadata.stripe_id)) {
|
|
||||||
const { id } = await this.stripe_.customers.create({
|
|
||||||
email: customer.email,
|
|
||||||
})
|
|
||||||
await this.customerService_.setMetadata(customer._id, "stripe_id", id)
|
|
||||||
} else {
|
|
||||||
stripeCustomerId = customer.metadata.stripe_id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const amount = await this.totalsService_.getTotal(cart)
|
const amount = await this.totalsService_.getTotal(cart)
|
||||||
const paymentIntent = await this.stripe_.paymentIntents.create({
|
|
||||||
customer: stripeCustomerId,
|
const intentRequest = {
|
||||||
amount: parseInt(amount * 100), // Stripe amount is in cents
|
amount: amount,
|
||||||
currency: currency_code,
|
currency: currency_code,
|
||||||
setup_future_usage: "on_session",
|
setup_future_usage: "on_session",
|
||||||
capture_method: "manual",
|
capture_method: this.options_.capture ? "automatic" : "manual",
|
||||||
metadata: { cart_id: `${cart._id}` },
|
metadata: { cart_id: `${cart.id}` },
|
||||||
})
|
}
|
||||||
|
|
||||||
|
if (customer_id) {
|
||||||
|
const customer = await this.customerService_.retrieve(customer_id)
|
||||||
|
|
||||||
|
if (customer.metadata?.stripe_id) {
|
||||||
|
intentRequest.customer = customer.metadata.stripe_id
|
||||||
|
} else {
|
||||||
|
const stripeCustomer = await this.createCustomer({
|
||||||
|
email,
|
||||||
|
id: customer_id,
|
||||||
|
})
|
||||||
|
|
||||||
|
intentRequest.customer = stripeCustomer.id
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const stripeCustomer = await this.createCustomer({
|
||||||
|
email,
|
||||||
|
})
|
||||||
|
|
||||||
|
intentRequest.customer = stripeCustomer.id
|
||||||
|
}
|
||||||
|
|
||||||
|
const paymentIntent = await this.stripe_.paymentIntents.create(
|
||||||
|
intentRequest
|
||||||
|
)
|
||||||
|
|
||||||
return paymentIntent
|
return paymentIntent
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves Stripe PaymentIntent.
|
* Retrieves Stripe payment intent.
|
||||||
* @param {object} data - the data of the payment to retrieve
|
* @param {object} data - the data of the payment to retrieve
|
||||||
* @returns {Object} Stripe PaymentIntent
|
* @returns {Promise<object>} Stripe payment intent
|
||||||
*/
|
*/
|
||||||
async retrievePayment(data) {
|
async retrievePayment(data) {
|
||||||
try {
|
try {
|
||||||
@@ -142,26 +190,74 @@ class StripeProviderService extends PaymentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates Stripe PaymentIntent.
|
* Gets a Stripe payment intent and returns it.
|
||||||
* @param {object} data - The payment session data.
|
* @param {object} sessionData - the data of the payment to retrieve
|
||||||
* @param {Object} cart - the current cart value
|
* @returns {Promise<object>} Stripe payment intent
|
||||||
* @returns {Object} Stripe PaymentIntent
|
|
||||||
*/
|
*/
|
||||||
async updatePayment(data, cart) {
|
async getPaymentData(sessionData) {
|
||||||
try {
|
try {
|
||||||
const { id } = data
|
return this.stripe_.paymentIntents.retrieve(sessionData.data.id)
|
||||||
const amount = await this.totalsService_.getTotal(cart)
|
} catch (error) {
|
||||||
return this.stripe_.paymentIntents.update(id, {
|
throw error
|
||||||
amount: parseInt(amount * 100),
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authorizes Stripe payment intent by simply returning
|
||||||
|
* the status for the payment intent in use.
|
||||||
|
* @param {object} sessionData - payment session data
|
||||||
|
* @param {object} context - properties relevant to current context
|
||||||
|
* @returns {Promise<{ status: string, data: object }>} result with data and status
|
||||||
|
*/
|
||||||
|
async authorizePayment(sessionData, context = {}) {
|
||||||
|
const stat = await this.getStatus(sessionData.data)
|
||||||
|
|
||||||
|
try {
|
||||||
|
return { data: sessionData.data, status: stat }
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async updatePaymentData(sessionData, update) {
|
||||||
|
try {
|
||||||
|
return this.stripe_.paymentIntents.update(sessionData.id, {
|
||||||
|
...update.data,
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async deletePayment(data) {
|
/**
|
||||||
|
* Updates Stripe payment intent.
|
||||||
|
* @param {object} sessionData - payment session data.
|
||||||
|
* @param {object} update - objec to update intent with
|
||||||
|
* @returns {object} Stripe payment intent
|
||||||
|
*/
|
||||||
|
async updatePayment(sessionData, cart) {
|
||||||
try {
|
try {
|
||||||
const { id } = data
|
const stripeId = cart.customer?.metadata?.stripe_id || undefined
|
||||||
|
|
||||||
|
if (stripeId !== sessionData.customer) {
|
||||||
|
return this.createPayment(cart)
|
||||||
|
} else {
|
||||||
|
if (cart.total && sessionData.amount === cart.total) {
|
||||||
|
return sessionData
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.stripe_.paymentIntents.update(sessionData.id, {
|
||||||
|
amount: cart.total,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async deletePayment(payment) {
|
||||||
|
try {
|
||||||
|
const { id } = payment.data
|
||||||
return this.stripe_.paymentIntents.cancel(id).catch((err) => {
|
return this.stripe_.paymentIntents.cancel(id).catch((err) => {
|
||||||
if (err.statusCode === 400) {
|
if (err.statusCode === 400) {
|
||||||
return
|
return
|
||||||
@@ -174,15 +270,15 @@ class StripeProviderService extends PaymentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates customer of Stripe PaymentIntent.
|
* Updates customer of Stripe payment intent.
|
||||||
* @param {string} cart - the cart to update payment intent for
|
* @param {string} paymentIntentId - id of payment intent to update
|
||||||
* @param {Object} data - the update object for the payment intent
|
* @param {string} customerId - id of new Stripe customer
|
||||||
* @returns {Object} Stripe PaymentIntent
|
* @returns {object} Stripe payment intent
|
||||||
*/
|
*/
|
||||||
async updatePaymentIntentCustomer(paymentIntent, id) {
|
async updatePaymentIntentCustomer(paymentIntentId, customerId) {
|
||||||
try {
|
try {
|
||||||
return this.stripe_.paymentIntents.update(paymentIntent, {
|
return this.stripe_.paymentIntents.update(paymentIntentId, {
|
||||||
customer: id,
|
customer: customerId,
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error
|
throw error
|
||||||
@@ -190,12 +286,12 @@ class StripeProviderService extends PaymentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Captures payment for Stripe PaymentIntent.
|
* Captures payment for Stripe payment intent.
|
||||||
* @param {Object} paymentData - payment method data from cart
|
* @param {object} paymentData - payment method data from cart
|
||||||
* @returns {Object} Stripe PaymentIntent
|
* @returns {object} Stripe payment intent
|
||||||
*/
|
*/
|
||||||
async capturePayment(paymentData) {
|
async capturePayment(payment) {
|
||||||
const { id } = paymentData
|
const { id } = payment.data
|
||||||
try {
|
try {
|
||||||
return this.stripe_.paymentIntents.capture(id)
|
return this.stripe_.paymentIntents.capture(id)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -204,32 +300,34 @@ class StripeProviderService extends PaymentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refunds payment for Stripe PaymentIntent.
|
* Refunds payment for Stripe payment intent.
|
||||||
* @param {Object} paymentData - payment method data from cart
|
* @param {object} paymentData - payment method data from cart
|
||||||
* @returns {string} id of payment intent
|
* @param {number} amountToRefund - amount to refund
|
||||||
|
* @returns {string} refunded payment intent
|
||||||
*/
|
*/
|
||||||
async refundPayment(paymentData, amount) {
|
async refundPayment(payment, amountToRefund) {
|
||||||
const { id } = paymentData
|
const { id } = payment.data
|
||||||
try {
|
try {
|
||||||
return this.stripe_.refunds.create({
|
await this.stripe_.refunds.create({
|
||||||
amount: parseInt(amount * 100),
|
amount: amountToRefund,
|
||||||
payment_intent: id,
|
payment_intent: id,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return payment.data
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancels payment for Stripe PaymentIntent.
|
* Cancels payment for Stripe payment intent.
|
||||||
* @param {Object} paymentData - payment method data from cart
|
* @param {object} paymentData - payment method data from cart
|
||||||
* @returns {string} id of payment intent
|
* @returns {object} canceled payment intent
|
||||||
*/
|
*/
|
||||||
async cancelPayment(paymentData) {
|
async cancelPayment(payment) {
|
||||||
const { id } = paymentData
|
const { id } = payment.data
|
||||||
try {
|
try {
|
||||||
const result = await this.stripe_.paymentIntents.cancel(id)
|
return this.stripe_.paymentIntents.cancel(id)
|
||||||
return result
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.payment_intent.status === "canceled") {
|
if (error.payment_intent.status === "canceled") {
|
||||||
return error.payment_intent
|
return error.payment_intent
|
||||||
@@ -241,10 +339,10 @@ class StripeProviderService extends PaymentService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs Stripe Webhook event
|
* Constructs Stripe Webhook event
|
||||||
* @param {Object} data - the data of the webhook request: req.body
|
* @param {object} data - the data of the webhook request: req.body
|
||||||
* @param {Object} signature - the Stripe signature on the event, that
|
* @param {object} signature - the Stripe signature on the event, that
|
||||||
* ensures integrity of the webhook event
|
* ensures integrity of the webhook event
|
||||||
* @returns {Object} Stripe Webhook event
|
* @returns {object} Stripe Webhook event
|
||||||
*/
|
*/
|
||||||
constructWebhookEvent(data, signature) {
|
constructWebhookEvent(data, signature) {
|
||||||
return this.stripe_.webhooks.constructEvent(
|
return this.stripe_.webhooks.constructEvent(
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
import { IdMap } from "medusa-test-utils"
|
|
||||||
import { carts, CartServiceMock } from "../../__mocks__/cart"
|
|
||||||
import { CustomerServiceMock } from "../../__mocks__/customer"
|
|
||||||
import { StripeProviderServiceMock } from "../../services/__mocks__/stripe-provider"
|
|
||||||
import { EventBusServiceMock } from "../../__mocks__/eventbus"
|
|
||||||
import CartSubscriber from "../cart"
|
|
||||||
|
|
||||||
describe("CartSubscriber", () => {
|
|
||||||
describe("onCustomerUpdated", () => {
|
|
||||||
let cartSubcriber = new CartSubscriber({
|
|
||||||
eventBusService: EventBusServiceMock,
|
|
||||||
cartService: CartServiceMock,
|
|
||||||
stripeProviderService: StripeProviderServiceMock,
|
|
||||||
customerService: CustomerServiceMock,
|
|
||||||
})
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
jest.clearAllMocks()
|
|
||||||
})
|
|
||||||
|
|
||||||
it("resolves on non-existing payment data", async () => {
|
|
||||||
await cartSubcriber.onCustomerUpdated(carts.emptyCart)
|
|
||||||
|
|
||||||
expect(CustomerServiceMock.retrieve).toHaveBeenCalledTimes(0)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("cancels old and creates new payment intent with the updated existing customer", async () => {
|
|
||||||
await cartSubcriber.onCustomerUpdated(carts.frCart)
|
|
||||||
|
|
||||||
expect(CustomerServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
|
||||||
expect(CustomerServiceMock.retrieve).toHaveBeenCalledWith(
|
|
||||||
IdMap.getId("lebron")
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(StripeProviderServiceMock.retrievePayment).toHaveBeenCalledTimes(1)
|
|
||||||
expect(StripeProviderServiceMock.retrievePayment).toHaveBeenCalledWith(
|
|
||||||
carts.frCart.payment_sessions[0].data
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(StripeProviderServiceMock.cancelPayment).toHaveBeenCalledTimes(1)
|
|
||||||
expect(StripeProviderServiceMock.cancelPayment).toHaveBeenCalledWith({
|
|
||||||
id: "pi",
|
|
||||||
customer: "cus_123456789",
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(StripeProviderServiceMock.createPayment).toHaveBeenCalledTimes(1)
|
|
||||||
expect(StripeProviderServiceMock.createPayment).toHaveBeenCalledWith(
|
|
||||||
carts.frCart
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(CartServiceMock.updatePaymentSession).toHaveBeenCalledTimes(1)
|
|
||||||
expect(CartServiceMock.updatePaymentSession).toHaveBeenCalledWith(
|
|
||||||
IdMap.getId("fr-cart"),
|
|
||||||
"stripe",
|
|
||||||
{
|
|
||||||
id: "pi_new",
|
|
||||||
customer: "cus_123456789_new",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("cancels old and creates new payment intent and creates new stripe customer", async () => {
|
|
||||||
await cartSubcriber.onCustomerUpdated(carts.frCartNoStripeCustomer)
|
|
||||||
|
|
||||||
expect(CustomerServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
|
||||||
expect(CustomerServiceMock.retrieve).toHaveBeenCalledWith(
|
|
||||||
IdMap.getId("vvd")
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(StripeProviderServiceMock.retrievePayment).toHaveBeenCalledTimes(1)
|
|
||||||
expect(StripeProviderServiceMock.retrievePayment).toHaveBeenCalledWith(
|
|
||||||
carts.frCartNoStripeCustomer.payment_sessions[0].data
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(StripeProviderServiceMock.createCustomer).toHaveBeenCalledTimes(1)
|
|
||||||
expect(StripeProviderServiceMock.createCustomer).toHaveBeenCalledWith({
|
|
||||||
_id: IdMap.getId("vvd"),
|
|
||||||
first_name: "Virgil",
|
|
||||||
last_name: "Van Dijk",
|
|
||||||
email: "virg@vvd.com",
|
|
||||||
password_hash: "1234",
|
|
||||||
metadata: {},
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(
|
|
||||||
StripeProviderServiceMock.updatePaymentIntentCustomer
|
|
||||||
).toHaveBeenCalledTimes(1)
|
|
||||||
expect(
|
|
||||||
StripeProviderServiceMock.updatePaymentIntentCustomer
|
|
||||||
).toHaveBeenCalledWith("cus_123456789_new_vvd")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -2,12 +2,12 @@ class CartSubscriber {
|
|||||||
constructor({
|
constructor({
|
||||||
cartService,
|
cartService,
|
||||||
customerService,
|
customerService,
|
||||||
stripeProviderService,
|
paymentProviderService,
|
||||||
eventBusService,
|
eventBusService,
|
||||||
}) {
|
}) {
|
||||||
this.cartService_ = cartService
|
this.cartService_ = cartService
|
||||||
this.customerService_ = customerService
|
this.customerService_ = customerService
|
||||||
this.stripeProviderService_ = stripeProviderService
|
this.paymentProviderService_ = paymentProviderService
|
||||||
this.eventBus_ = eventBusService
|
this.eventBus_ = eventBusService
|
||||||
|
|
||||||
this.eventBus_.subscribe("cart.customer_updated", async (cart) => {
|
this.eventBus_.subscribe("cart.customer_updated", async (cart) => {
|
||||||
@@ -15,64 +15,37 @@ class CartSubscriber {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async onCustomerUpdated(cart) {
|
async onCustomerUpdated(cartId) {
|
||||||
const { customer_id, payment_sessions } = cart
|
const cart = await this.cartService_.retrieve(cartId, {
|
||||||
|
select: [
|
||||||
|
"subtotal",
|
||||||
|
"tax_total",
|
||||||
|
"shipping_total",
|
||||||
|
"discount_total",
|
||||||
|
"total",
|
||||||
|
],
|
||||||
|
relations: [
|
||||||
|
"items",
|
||||||
|
"billing_address",
|
||||||
|
"shipping_address",
|
||||||
|
"region",
|
||||||
|
"region.payment_providers",
|
||||||
|
"payment_sessions",
|
||||||
|
"customer",
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
if (!payment_sessions) {
|
if (!cart.payment_sessions?.length) {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
const customer = await this.customerService_.retrieve(customer_id)
|
const session = cart.payment_sessions.find(
|
||||||
|
(ps) => ps.provider_id === "stripe"
|
||||||
const stripeSession = payment_sessions.find(
|
|
||||||
(s) => s.provider_id === "stripe"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!stripeSession) {
|
if (session) {
|
||||||
return Promise.resolve()
|
return this.paymentProviderService_.updateSession(session, cart)
|
||||||
}
|
}
|
||||||
|
|
||||||
const paymentIntent = await this.stripeProviderService_.retrievePayment(
|
|
||||||
stripeSession.data
|
|
||||||
)
|
|
||||||
|
|
||||||
let stripeCustomer
|
|
||||||
if (customer.metadata && customer.metadata.stripe_id) {
|
|
||||||
stripeCustomer = await this.stripeProviderService_.retrieveCustomer(
|
|
||||||
customer.metadata.stripe_id
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!stripeCustomer) {
|
|
||||||
stripeCustomer = await this.stripeProviderService_.createCustomer(
|
|
||||||
customer
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stripeCustomer.id === paymentIntent.customer) {
|
|
||||||
return Promise.resolve()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!paymentIntent.customer) {
|
|
||||||
return this.stripeProviderService_.updatePaymentIntentCustomer(
|
|
||||||
stripeCustomer.id
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stripeCustomer.id !== paymentIntent.customer) {
|
|
||||||
await this.stripeProviderService_.cancelPayment(paymentIntent)
|
|
||||||
const newPaymentIntent = await this.stripeProviderService_.createPayment(
|
|
||||||
cart
|
|
||||||
)
|
|
||||||
|
|
||||||
await this.cartService_.updatePaymentSession(
|
|
||||||
cart._id,
|
|
||||||
"stripe",
|
|
||||||
newPaymentIntent
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.resolve()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-plugin-add-ons",
|
"name": "medusa-plugin-add-ons",
|
||||||
"version": "1.0.8",
|
"version": "1.0.7-alpha.176+0646bd3",
|
||||||
"description": "Add-on plugin for Medusa Commerce",
|
"description": "Add-on plugin for Medusa Commerce",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
"cross-env": "^7.0.2",
|
"cross-env": "^7.0.2",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"jest": "^25.5.2",
|
"jest": "^25.5.2",
|
||||||
"medusa-test-utils": "^1.0.13"
|
"medusa-test-utils": "^1.0.12-alpha.176+0646bd3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "babel src -d . --ignore **/__tests__",
|
"build": "babel src -d . --ignore **/__tests__",
|
||||||
@@ -37,8 +37,8 @@
|
|||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"medusa-core-utils": "^1.0.11",
|
"medusa-core-utils": "^1.0.12-alpha.787+0646bd3",
|
||||||
"redis": "^3.0.2"
|
"redis": "^3.0.2"
|
||||||
},
|
},
|
||||||
"gitHead": "3bd91f65304ed1d31c41b85d5c87123450e0542e"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -39,16 +39,7 @@ class AddOnService extends BaseService {
|
|||||||
* @return {string} the validated id
|
* @return {string} the validated id
|
||||||
*/
|
*/
|
||||||
validateId_(rawId) {
|
validateId_(rawId) {
|
||||||
const schema = Validator.objectId()
|
return rawId
|
||||||
const { value, error } = schema.validate(rawId.toString())
|
|
||||||
if (error) {
|
|
||||||
throw new MedusaError(
|
|
||||||
MedusaError.Types.INVALID_ARGUMENT,
|
|
||||||
"The addOnId could not be casted to an ObjectId"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -187,7 +178,7 @@ class AddOnService extends BaseService {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Return the price if we found a suitable match
|
// Return the price if we found a suitable match
|
||||||
if (price) {
|
if (price !== undefined) {
|
||||||
return price
|
return price
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-plugin-brightpearl",
|
"name": "medusa-plugin-brightpearl",
|
||||||
"version": "1.0.26",
|
"version": "1.0.21-alpha.176+0646bd3",
|
||||||
"description": "Brightpearl plugin for Medusa Commerce",
|
"description": "Brightpearl plugin for Medusa Commerce",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
"cross-env": "^7.0.2",
|
"cross-env": "^7.0.2",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"jest": "^25.5.2",
|
"jest": "^25.5.2",
|
||||||
"medusa-test-utils": "^1.0.13",
|
"medusa-test-utils": "^1.0.12-alpha.176+0646bd3",
|
||||||
"prettier": "^2.0.5"
|
"prettier": "^2.0.5"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -43,8 +43,8 @@
|
|||||||
"axios": "^0.19.2",
|
"axios": "^0.19.2",
|
||||||
"axios-rate-limit": "^1.2.1",
|
"axios-rate-limit": "^1.2.1",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"medusa-core-utils": "^1.0.11",
|
"medusa-core-utils": "^1.0.12-alpha.787+0646bd3",
|
||||||
"randomatic": "^3.1.1"
|
"randomatic": "^3.1.1"
|
||||||
},
|
},
|
||||||
"gitHead": "3bd91f65304ed1d31c41b85d5c87123450e0542e"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
const REFRESH_CRON = process.env.BP_REFRESH_CRON || "5 4 * * */6"
|
|
||||||
|
|
||||||
const refreshToken = async (container) => {
|
|
||||||
const logger = container.resolve("logger")
|
|
||||||
const oauthService = container.resolve("oauthService")
|
|
||||||
const eventBus = container.resolve("eventBusService")
|
|
||||||
|
|
||||||
try {
|
|
||||||
logger.info("registering refresh cron job BP")
|
|
||||||
eventBus.createCronJob("refresh-token-bp", {}, REFRESH_CRON, async () => {
|
|
||||||
const appData = await oauthService.retrieveByName("brightpearl")
|
|
||||||
const data = appData.data
|
|
||||||
if (data && data.refresh_token) {
|
|
||||||
return oauthService.refreshToken("brightpearl", data.refresh_token)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} catch (err) {
|
|
||||||
if (err.name === "not_allowed") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default refreshToken
|
|
||||||
@@ -6,8 +6,52 @@ import MockAdapter from "axios-mock-adapter"
|
|||||||
|
|
||||||
jest.mock("../../utils/brightpearl")
|
jest.mock("../../utils/brightpearl")
|
||||||
|
|
||||||
|
const order = {
|
||||||
|
region: {
|
||||||
|
tax_code: "1234",
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
title: "Test",
|
||||||
|
variant: {
|
||||||
|
sku: "TEST",
|
||||||
|
},
|
||||||
|
unit_price: 1100,
|
||||||
|
quantity: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
shipping_total: 12399,
|
||||||
|
shipping_methods: [
|
||||||
|
{
|
||||||
|
name: "standard",
|
||||||
|
price: 12399,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
payment_method: {
|
||||||
|
id: "123",
|
||||||
|
},
|
||||||
|
tax_rate: 23.1,
|
||||||
|
currency_code: "DKK",
|
||||||
|
display_id: "1234",
|
||||||
|
id: "12355",
|
||||||
|
discounts: [],
|
||||||
|
shipping_address: {
|
||||||
|
first_name: "Test",
|
||||||
|
last_name: "Testson",
|
||||||
|
address_1: "Test",
|
||||||
|
address_2: "TEst",
|
||||||
|
postal_code: "1234",
|
||||||
|
country_code: "DK",
|
||||||
|
phone: "12345678",
|
||||||
|
},
|
||||||
|
email: "test@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
const OrderService = {
|
const OrderService = {
|
||||||
setMetadata: () => {
|
retrieve: () => {
|
||||||
|
return Promise.resolve(order)
|
||||||
|
},
|
||||||
|
update: () => {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -20,7 +64,7 @@ const TotalsService = {
|
|||||||
return Promise.resolve([])
|
return Promise.resolve([])
|
||||||
},
|
},
|
||||||
getShippingTotal: () => {
|
getShippingTotal: () => {
|
||||||
return 123.9999929393293
|
return 12399
|
||||||
},
|
},
|
||||||
rounded: (value) => {
|
rounded: (value) => {
|
||||||
const decimalPlaces = 4
|
const decimalPlaces = 4
|
||||||
@@ -91,45 +135,6 @@ describe("BrightpearlService", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe("createSalesOrder", () => {
|
describe("createSalesOrder", () => {
|
||||||
const order = {
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: "Test",
|
|
||||||
content: {
|
|
||||||
variant: {
|
|
||||||
sku: "TEST",
|
|
||||||
},
|
|
||||||
unit_price: 11,
|
|
||||||
},
|
|
||||||
quantity: 2,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
shipping_methods: [
|
|
||||||
{
|
|
||||||
name: "standard",
|
|
||||||
price: 123.9999929393293,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
payment_method: {
|
|
||||||
_id: "123",
|
|
||||||
},
|
|
||||||
tax_rate: 0.231,
|
|
||||||
currency_code: "DKK",
|
|
||||||
display_id: "1234",
|
|
||||||
_id: "12355",
|
|
||||||
discounts: [],
|
|
||||||
shipping_address: {
|
|
||||||
first_name: "Test",
|
|
||||||
last_name: "Testson",
|
|
||||||
address_1: "Test",
|
|
||||||
address_2: "TEst",
|
|
||||||
postal_code: "1234",
|
|
||||||
country_code: "DK",
|
|
||||||
phone: "12345678",
|
|
||||||
},
|
|
||||||
email: "test@example.com",
|
|
||||||
}
|
|
||||||
|
|
||||||
const bpService = new BrightpearlService(
|
const bpService = new BrightpearlService(
|
||||||
{
|
{
|
||||||
orderService: OrderService,
|
orderService: OrderService,
|
||||||
@@ -187,8 +192,8 @@ describe("BrightpearlService", () => {
|
|||||||
{
|
{
|
||||||
name: "Shipping: standard",
|
name: "Shipping: standard",
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
net: 124,
|
net: 123.99,
|
||||||
tax: 28.644,
|
tax: 28.6417,
|
||||||
taxCode: "1234",
|
taxCode: "1234",
|
||||||
nominalCode: "4040",
|
nominalCode: "4040",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ class BrightpearlService extends BaseService {
|
|||||||
|
|
||||||
// Only update if the inventory levels have changed
|
// Only update if the inventory levels have changed
|
||||||
if (parseInt(v.inventory_quantity) !== parseInt(onHand)) {
|
if (parseInt(v.inventory_quantity) !== parseInt(onHand)) {
|
||||||
return this.productVariantService_.update(v._id, {
|
return this.productVariantService_.update(v.id, {
|
||||||
inventory_quantity: onHand,
|
inventory_quantity: onHand,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -181,10 +181,13 @@ class BrightpearlService extends BaseService {
|
|||||||
const onHand = availability[productId].total.onHand
|
const onHand = availability[productId].total.onHand
|
||||||
|
|
||||||
const sku = brightpearlProduct.identity.sku
|
const sku = brightpearlProduct.identity.sku
|
||||||
const [variant] = await this.productVariantService_.list({ sku })
|
if (!sku) return
|
||||||
|
|
||||||
|
const variant = await this.productVariantService_
|
||||||
|
.retrieveBySKU(sku)
|
||||||
|
.catch((_) => undefined)
|
||||||
if (variant && variant.manage_inventory) {
|
if (variant && variant.manage_inventory) {
|
||||||
await this.productVariantService_.update(variant._id, {
|
await this.productVariantService_.update(variant.id, {
|
||||||
inventory_quantity: onHand,
|
inventory_quantity: onHand,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -249,7 +252,7 @@ class BrightpearlService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async createRefundCredit(fromOrder, fromRefund) {
|
async createRefundCredit(fromOrder, fromRefund) {
|
||||||
const region = await this.regionService_.retrieve(fromOrder.region_id)
|
const region = fromOrder.region
|
||||||
const client = await this.getClient()
|
const client = await this.getClient()
|
||||||
const authData = await this.getAuthData()
|
const authData = await this.getAuthData()
|
||||||
const orderId = fromOrder.metadata.brightpearl_sales_order_id
|
const orderId = fromOrder.metadata.brightpearl_sales_order_id
|
||||||
@@ -266,7 +269,7 @@ class BrightpearlService extends BaseService {
|
|||||||
const order = {
|
const order = {
|
||||||
currency: parentSo.currency,
|
currency: parentSo.currency,
|
||||||
ref: parentSo.ref,
|
ref: parentSo.ref,
|
||||||
externalRef: `${parentSo.externalRef}.${fromOrder.refunds.length}`,
|
externalRef: `${parentSo.externalRef}.${fromRefund.id}`,
|
||||||
channelId: this.options.channel_id || `1`,
|
channelId: this.options.channel_id || `1`,
|
||||||
installedIntegrationInstanceId: authData.installation_instance_id,
|
installedIntegrationInstanceId: authData.installation_instance_id,
|
||||||
customer: parentSo.customer,
|
customer: parentSo.customer,
|
||||||
@@ -277,11 +280,12 @@ class BrightpearlService extends BaseService {
|
|||||||
name: `${fromRefund.reason}: ${fromRefund.note}`,
|
name: `${fromRefund.reason}: ${fromRefund.note}`,
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
taxCode: region.tax_code,
|
taxCode: region.tax_code,
|
||||||
net: this.totalsService_.rounded(
|
net: this.bpnum_(
|
||||||
fromRefund.amount / (1 + fromOrder.tax_rate)
|
fromRefund.amount,
|
||||||
|
10000 / (100 + fromOrder.tax_rate)
|
||||||
),
|
),
|
||||||
tax: this.totalsService_.rounded(
|
tax: this.bpnum_(
|
||||||
fromRefund.amount - fromRefund.amount / (1 + fromOrder.tax_rate)
|
fromRefund.amount * (1 - 100 / (100 + fromOrder.tax_rate))
|
||||||
),
|
),
|
||||||
nominalCode: accountingCode,
|
nominalCode: accountingCode,
|
||||||
},
|
},
|
||||||
@@ -291,15 +295,15 @@ class BrightpearlService extends BaseService {
|
|||||||
return client.orders
|
return client.orders
|
||||||
.createCredit(order)
|
.createCredit(order)
|
||||||
.then(async (creditId) => {
|
.then(async (creditId) => {
|
||||||
const paymentMethod = fromOrder.payment_method
|
const paymentMethod = fromOrder.payments[0]
|
||||||
const paymentType = "PAYMENT"
|
const paymentType = "PAYMENT"
|
||||||
const payment = {
|
const payment = {
|
||||||
transactionRef: `${paymentMethod._id}.${fromOrder.refunds.length}`,
|
transactionRef: `${paymentMethod.id}.${fromRefund.id}`,
|
||||||
transactionCode: fromOrder._id,
|
transactionCode: fromOrder.id,
|
||||||
paymentMethodCode: this.options.payment_method_code || "1220",
|
paymentMethodCode: this.options.payment_method_code || "1220",
|
||||||
orderId: creditId,
|
orderId: creditId,
|
||||||
currencyIsoCode: fromOrder.currency_code,
|
currencyIsoCode: fromOrder.currency_code.toUpperCase(),
|
||||||
amountPaid: fromRefund.amount,
|
amountPaid: this.bpnum_(fromRefund.amount),
|
||||||
paymentDate: new Date(),
|
paymentDate: new Date(),
|
||||||
paymentType,
|
paymentType,
|
||||||
}
|
}
|
||||||
@@ -309,18 +313,21 @@ class BrightpearlService extends BaseService {
|
|||||||
|
|
||||||
await client.payments.create(payment)
|
await client.payments.create(payment)
|
||||||
|
|
||||||
return this.orderService_.setMetadata(
|
return this.orderService_.update(fromOrder.id, {
|
||||||
fromOrder._id,
|
metadata: {
|
||||||
"brightpearl_credit_ids",
|
brightpearl_credit_ids: newIds,
|
||||||
newIds
|
},
|
||||||
)
|
})
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
console.log(err.response.data.errors)
|
||||||
})
|
})
|
||||||
.catch((err) => console.log(err.response.data.errors))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async createSalesCredit(fromOrder, fromReturn) {
|
async createSalesCredit(fromOrder, fromReturn) {
|
||||||
const region = await this.regionService_.retrieve(fromOrder.region_id)
|
const region = fromOrder.region
|
||||||
const client = await this.getClient()
|
const client = await this.getClient()
|
||||||
const authData = await this.getAuthData()
|
const authData = await this.getAuthData()
|
||||||
const orderId = fromOrder.metadata.brightpearl_sales_order_id
|
const orderId = fromOrder.metadata.brightpearl_sales_order_id
|
||||||
@@ -329,7 +336,7 @@ class BrightpearlService extends BaseService {
|
|||||||
const order = {
|
const order = {
|
||||||
currency: parentSo.currency,
|
currency: parentSo.currency,
|
||||||
ref: parentSo.ref,
|
ref: parentSo.ref,
|
||||||
externalRef: `${parentSo.externalRef}.${fromOrder.refunds.length}`,
|
externalRef: `${parentSo.externalRef}.${fromReturn.id}`,
|
||||||
channelId: this.options.channel_id || `1`,
|
channelId: this.options.channel_id || `1`,
|
||||||
installedIntegrationInstanceId: authData.installation_instance_id,
|
installedIntegrationInstanceId: authData.installation_instance_id,
|
||||||
customer: parentSo.customer,
|
customer: parentSo.customer,
|
||||||
@@ -359,18 +366,14 @@ class BrightpearlService extends BaseService {
|
|||||||
return acc + next.net + next.tax
|
return acc + next.net + next.tax
|
||||||
}, 0)
|
}, 0)
|
||||||
|
|
||||||
const difference = fromReturn.refund_amount - total
|
const difference = (fromReturn.refund_amount / 100 - total) * 100
|
||||||
if (difference) {
|
if (difference) {
|
||||||
order.rows.push({
|
order.rows.push({
|
||||||
name: "Difference",
|
name: "Difference",
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
taxCode: region.tax_code,
|
taxCode: region.tax_code,
|
||||||
net: this.totalsService_.rounded(
|
net: this.bpnum_(difference, 10000 / (100 + fromOrder.tax_rate)),
|
||||||
difference / (1 + fromOrder.tax_rate)
|
tax: this.bpnum_(difference * (1 - 100 / (100 + fromOrder.tax_rate))),
|
||||||
),
|
|
||||||
tax: this.totalsService_.rounded(
|
|
||||||
difference - difference / (1 + fromOrder.tax_rate)
|
|
||||||
),
|
|
||||||
nominalCode: this.options.sales_account_code || "4000",
|
nominalCode: this.options.sales_account_code || "4000",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -378,15 +381,15 @@ class BrightpearlService extends BaseService {
|
|||||||
return client.orders
|
return client.orders
|
||||||
.createCredit(order)
|
.createCredit(order)
|
||||||
.then(async (creditId) => {
|
.then(async (creditId) => {
|
||||||
const paymentMethod = fromOrder.payment_method
|
const paymentMethod = fromOrder.payments[0]
|
||||||
const paymentType = "PAYMENT"
|
const paymentType = "PAYMENT"
|
||||||
const payment = {
|
const payment = {
|
||||||
transactionRef: `${paymentMethod._id}.${fromOrder.refunds.length}`,
|
transactionRef: `${paymentMethod.id}.${fromReturn.id}`,
|
||||||
transactionCode: fromOrder._id,
|
transactionCode: fromOrder.id,
|
||||||
paymentMethodCode: this.options.payment_method_code || "1220",
|
paymentMethodCode: this.options.payment_method_code || "1220",
|
||||||
orderId: creditId,
|
orderId: creditId,
|
||||||
currencyIsoCode: fromOrder.currency_code,
|
currencyIsoCode: fromOrder.currency_code.toUpperCase(),
|
||||||
amountPaid: fromReturn.refund_amount,
|
amountPaid: this.bpnum_(fromReturn.refund_amount),
|
||||||
paymentDate: new Date(),
|
paymentDate: new Date(),
|
||||||
paymentType,
|
paymentType,
|
||||||
}
|
}
|
||||||
@@ -396,17 +399,34 @@ class BrightpearlService extends BaseService {
|
|||||||
|
|
||||||
await client.payments.create(payment)
|
await client.payments.create(payment)
|
||||||
|
|
||||||
return this.orderService_.setMetadata(
|
return this.orderService_.update(fromOrder.id, {
|
||||||
fromOrder._id,
|
metadata: {
|
||||||
"brightpearl_credit_ids",
|
brightpearl_credit_ids: newIds,
|
||||||
newIds
|
},
|
||||||
)
|
})
|
||||||
})
|
})
|
||||||
.catch((err) => console.log(err.response.data.errors))
|
.catch((err) => console.log(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async createSalesOrder(fromOrder) {
|
async createSalesOrder(fromOrderId) {
|
||||||
|
const fromOrder = await this.orderService_.retrieve(fromOrderId, {
|
||||||
|
select: [
|
||||||
|
"total",
|
||||||
|
"subtotal",
|
||||||
|
"shipping_total",
|
||||||
|
"gift_card_total",
|
||||||
|
"discount_total",
|
||||||
|
],
|
||||||
|
relations: [
|
||||||
|
"region",
|
||||||
|
"shipping_address",
|
||||||
|
"billing_address",
|
||||||
|
"shipping_methods",
|
||||||
|
"payments",
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
const client = await this.getClient()
|
const client = await this.getClient()
|
||||||
let customer = await this.retrieveCustomerByEmail(fromOrder.email)
|
let customer = await this.retrieveCustomerByEmail(fromOrder.email)
|
||||||
|
|
||||||
@@ -420,10 +440,10 @@ class BrightpearlService extends BaseService {
|
|||||||
const { shipping_address } = fromOrder
|
const { shipping_address } = fromOrder
|
||||||
const order = {
|
const order = {
|
||||||
currency: {
|
currency: {
|
||||||
code: fromOrder.currency_code,
|
code: fromOrder.currency_code.toUpperCase(),
|
||||||
},
|
},
|
||||||
ref: fromOrder.display_id,
|
ref: fromOrder.display_id,
|
||||||
externalRef: fromOrder._id,
|
externalRef: fromOrder.id,
|
||||||
channelId: this.options.channel_id || `1`,
|
channelId: this.options.channel_id || `1`,
|
||||||
installedIntegrationInstanceId: authData.installation_instance_id,
|
installedIntegrationInstanceId: authData.installation_instance_id,
|
||||||
statusId: this.options.default_status_id || `3`,
|
statusId: this.options.default_status_id || `3`,
|
||||||
@@ -464,33 +484,34 @@ class BrightpearlService extends BaseService {
|
|||||||
return salesOrderId
|
return salesOrderId
|
||||||
})
|
})
|
||||||
.then((salesOrderId) => {
|
.then((salesOrderId) => {
|
||||||
return this.orderService_.setMetadata(
|
return this.orderService_.update(fromOrder.id, {
|
||||||
fromOrder._id,
|
metadata: {
|
||||||
"brightpearl_sales_order_id",
|
brightpearl_sales_order_id: salesOrderId,
|
||||||
salesOrderId
|
},
|
||||||
)
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async createSwapPayment(fromSwap) {
|
async createSwapPayment(fromSwapId) {
|
||||||
|
const fromSwap = await this.swapService_.retrieve(fromSwapId)
|
||||||
|
|
||||||
const client = await this.getClient()
|
const client = await this.getClient()
|
||||||
const soId =
|
const soId =
|
||||||
fromSwap.metadata && fromSwap.metadata.brightpearl_sales_order_id
|
fromSwap.metadata && fromSwap.metadata.brightpearl_sales_order_id
|
||||||
|
|
||||||
if (!soId || fromSwap.amount_paid <= 0) {
|
if (!soId || fromSwap.difference_due <= 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const paymentType = "RECEIPT"
|
const paymentType = "RECEIPT"
|
||||||
const paymentMethod = fromSwap.payment_method
|
|
||||||
const payment = {
|
const payment = {
|
||||||
transactionRef: `${paymentMethod._id}.${paymentType}`, // Brightpearl cannot accept an auth and capture with same ref
|
transactionRef: `${fromSwap.id}.${paymentType}`, // Brightpearl cannot accept an auth and capture with same ref
|
||||||
transactionCode: fromSwap._id,
|
transactionCode: fromSwap.id,
|
||||||
paymentMethodCode: this.options.payment_method_code || "1220",
|
paymentMethodCode: this.options.payment_method_code || "1220",
|
||||||
orderId: soId,
|
orderId: soId,
|
||||||
paymentDate: new Date(),
|
paymentDate: new Date(),
|
||||||
currencyIsoCode: fromSwap.currency_code,
|
currencyIsoCode: fromSwap.order.currency_code.toUpperCase(),
|
||||||
amountPaid: fromSwap.amount_paid,
|
amountPaid: fromSwap.difference_due,
|
||||||
paymentType,
|
paymentType,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -502,20 +523,23 @@ class BrightpearlService extends BaseService {
|
|||||||
let customer = await this.retrieveCustomerByEmail(fromOrder.email)
|
let customer = await this.retrieveCustomerByEmail(fromOrder.email)
|
||||||
|
|
||||||
if (!customer) {
|
if (!customer) {
|
||||||
customer = await this.createCustomer(fromOrder)
|
customer = await this.createCustomer({
|
||||||
|
...fromOrder,
|
||||||
|
...fromSwap,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const authData = await this.getAuthData()
|
const authData = await this.getAuthData()
|
||||||
|
|
||||||
const sIndex = fromOrder.swaps.findIndex((s) => fromSwap._id.equals(s))
|
const sIndex = fromOrder.swaps.findIndex((s) => fromSwap.id === s.id)
|
||||||
|
|
||||||
const { shipping_address } = fromSwap
|
const { shipping_address } = fromSwap
|
||||||
const order = {
|
const order = {
|
||||||
currency: {
|
currency: {
|
||||||
code: fromOrder.currency_code,
|
code: fromOrder.currency_code.toUpperCase(),
|
||||||
},
|
},
|
||||||
ref: `${fromOrder.display_id}-S${sIndex + 1}`,
|
ref: `${fromOrder.display_id}-S${sIndex + 1}`,
|
||||||
externalRef: `${fromOrder._id}.${fromSwap._id}`,
|
externalRef: `${fromOrder.id}.${fromSwap.id}`,
|
||||||
channelId: this.options.channel_id || `1`,
|
channelId: this.options.channel_id || `1`,
|
||||||
installedIntegrationInstanceId: authData.installation_instance_id,
|
installedIntegrationInstanceId: authData.installation_instance_id,
|
||||||
statusId:
|
statusId:
|
||||||
@@ -545,7 +569,7 @@ class BrightpearlService extends BaseService {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
rows: await this.getBrightpearlRows({
|
rows: await this.getBrightpearlRows({
|
||||||
region_id: fromOrder.region_id,
|
region: fromOrder.region,
|
||||||
discounts: fromOrder.discounts,
|
discounts: fromOrder.discounts,
|
||||||
tax_rate: fromOrder.tax_rate,
|
tax_rate: fromOrder.tax_rate,
|
||||||
items: fromSwap.additional_items,
|
items: fromSwap.additional_items,
|
||||||
@@ -562,14 +586,14 @@ class BrightpearlService extends BaseService {
|
|||||||
return acc + parseFloat(next.net) + parseFloat(next.tax)
|
return acc + parseFloat(next.net) + parseFloat(next.tax)
|
||||||
}, 0)
|
}, 0)
|
||||||
|
|
||||||
const paymentMethod = fromOrder.payment_method
|
const paymentMethod = fromOrder.payments[0]
|
||||||
const paymentType = "RECEIPT"
|
const paymentType = "RECEIPT"
|
||||||
const payment = {
|
const payment = {
|
||||||
transactionRef: `${paymentMethod._id}.${paymentType}-${fromSwap._id}`,
|
transactionRef: `${paymentMethod.id}.${paymentType}-${fromSwap.id}`,
|
||||||
transactionCode: fromOrder._id,
|
transactionCode: fromOrder.id,
|
||||||
paymentMethodCode: this.options.payment_method_code || "1220",
|
paymentMethodCode: this.options.payment_method_code || "1220",
|
||||||
orderId: salesOrderId,
|
orderId: salesOrderId,
|
||||||
currencyIsoCode: fromOrder.currency_code,
|
currencyIsoCode: fromOrder.currency_code.toUpperCase(),
|
||||||
amountPaid: total,
|
amountPaid: total,
|
||||||
paymentDate: new Date(),
|
paymentDate: new Date(),
|
||||||
paymentType,
|
paymentType,
|
||||||
@@ -577,33 +601,33 @@ class BrightpearlService extends BaseService {
|
|||||||
|
|
||||||
await client.payments.create(payment)
|
await client.payments.create(payment)
|
||||||
|
|
||||||
return this.swapService_.setMetadata(
|
return this.swapService_.update(fromSwap.id, {
|
||||||
fromSwap._id,
|
metadata: {
|
||||||
"brightpearl_sales_order_id",
|
brightpearl_sales_order_id: salesOrderId,
|
||||||
salesOrderId
|
},
|
||||||
)
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async createSwapCredit(fromOrder, fromSwap) {
|
async createSwapCredit(fromOrder, fromSwap) {
|
||||||
const region = await this.regionService_.retrieve(fromOrder.region_id)
|
const region = fromOrder.region
|
||||||
const client = await this.getClient()
|
const client = await this.getClient()
|
||||||
const authData = await this.getAuthData()
|
const authData = await this.getAuthData()
|
||||||
const orderId = fromOrder.metadata.brightpearl_sales_order_id
|
const orderId = fromOrder.metadata.brightpearl_sales_order_id
|
||||||
const sIndex = fromOrder.swaps.findIndex((s) => fromSwap._id.equals(s))
|
const sIndex = fromOrder.swaps.findIndex((s) => fromSwap.id === s.id)
|
||||||
|
|
||||||
if (orderId) {
|
if (orderId) {
|
||||||
const parentSo = await client.orders.retrieve(orderId)
|
const parentSo = await client.orders.retrieve(orderId)
|
||||||
const order = {
|
const order = {
|
||||||
currency: parentSo.currency,
|
currency: parentSo.currency,
|
||||||
ref: `${parentSo.ref}-S${sIndex + 1}`,
|
ref: `${parentSo.ref}-S${sIndex + 1}`,
|
||||||
externalRef: `${parentSo.externalRef}.${fromSwap._id}`,
|
externalRef: `${parentSo.externalRef}.${fromSwap.id}`,
|
||||||
channelId: this.options.channel_id || `1`,
|
channelId: this.options.channel_id || `1`,
|
||||||
installedIntegrationInstanceId: authData.installation_instance_id,
|
installedIntegrationInstanceId: authData.installation_instance_id,
|
||||||
customer: parentSo.customer,
|
customer: parentSo.customer,
|
||||||
delivery: parentSo.delivery,
|
delivery: parentSo.delivery,
|
||||||
parentId: orderId,
|
parentId: orderId,
|
||||||
rows: fromSwap.return.items.map((i) => {
|
rows: fromSwap.return_order.items.map((i) => {
|
||||||
const parentRow = parentSo.rows.find((row) => {
|
const parentRow = parentSo.rows.find((row) => {
|
||||||
return row.externalRef === i.item_id
|
return row.externalRef === i.item_id
|
||||||
})
|
})
|
||||||
@@ -623,13 +647,18 @@ class BrightpearlService extends BaseService {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fromSwap.return_shipping && fromSwap.return_shipping.price) {
|
if (
|
||||||
|
fromSwap.return_order.shipping_method &&
|
||||||
|
fromSwap.return_order.shipping_method.price
|
||||||
|
) {
|
||||||
order.rows.push({
|
order.rows.push({
|
||||||
name: "Return Shipping",
|
name: "Return Shipping",
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
taxCode: region.tax_code,
|
taxCode: region.tax_code,
|
||||||
net: -1 * fromSwap.return_shipping.price,
|
net: (-1 * fromSwap.return_order.shipping_method.price) / 100,
|
||||||
tax: -1 * fromSwap.return_shipping.price * fromOrder.tax_rate,
|
tax:
|
||||||
|
((-1 * fromSwap.return_order.shipping_method.price) / 100) *
|
||||||
|
(fromOrder.tax_rate / 100),
|
||||||
nominalCode: this.options.shipping_account_code || "4040",
|
nominalCode: this.options.shipping_account_code || "4040",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -641,14 +670,14 @@ class BrightpearlService extends BaseService {
|
|||||||
return client.orders
|
return client.orders
|
||||||
.createCredit(order)
|
.createCredit(order)
|
||||||
.then(async (creditId) => {
|
.then(async (creditId) => {
|
||||||
const paymentMethod = fromOrder.payment_method
|
const paymentMethod = fromOrder.payments[0]
|
||||||
const paymentType = "PAYMENT"
|
const paymentType = "PAYMENT"
|
||||||
const payment = {
|
const payment = {
|
||||||
transactionRef: `${paymentMethod._id}.${paymentType}-${fromSwap._id}`,
|
transactionRef: `${paymentMethod.id}.${paymentType}-${fromSwap.id}`,
|
||||||
transactionCode: fromSwap._id,
|
transactionCode: fromSwap.id,
|
||||||
paymentMethodCode: this.options.payment_method_code || "1220",
|
paymentMethodCode: this.options.payment_method_code || "1220",
|
||||||
orderId: creditId,
|
orderId: creditId,
|
||||||
currencyIsoCode: fromSwap.currency_code,
|
currencyIsoCode: fromOrder.currency_code.toUpperCase(),
|
||||||
amountPaid: total,
|
amountPaid: total,
|
||||||
paymentDate: new Date(),
|
paymentDate: new Date(),
|
||||||
paymentType,
|
paymentType,
|
||||||
@@ -660,7 +689,12 @@ class BrightpearlService extends BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async createPayment(fromOrder) {
|
async createPayment(fromOrderId) {
|
||||||
|
const fromOrder = await this.orderService_.retrieve(fromOrderId, {
|
||||||
|
select: ["total"],
|
||||||
|
relations: ["payments"],
|
||||||
|
})
|
||||||
|
|
||||||
const client = await this.getClient()
|
const client = await this.getClient()
|
||||||
const soId =
|
const soId =
|
||||||
fromOrder.metadata && fromOrder.metadata.brightpearl_sales_order_id
|
fromOrder.metadata && fromOrder.metadata.brightpearl_sales_order_id
|
||||||
@@ -669,38 +703,35 @@ class BrightpearlService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const paymentType = "RECEIPT"
|
const paymentType = "RECEIPT"
|
||||||
const paymentMethod = fromOrder.payment_method
|
|
||||||
const payment = {
|
const payment = {
|
||||||
transactionRef: `${paymentMethod._id}.${paymentType}`, // Brightpearl cannot accept an auth and capture with same ref
|
transactionRef: `${fromOrder.id}.${paymentType}`, // Brightpearl cannot accept an auth and capture with same ref
|
||||||
transactionCode: fromOrder._id,
|
transactionCode: fromOrder.id,
|
||||||
paymentMethodCode: this.options.payment_method_code || "1220",
|
paymentMethodCode: this.options.payment_method_code || "1220",
|
||||||
orderId: soId,
|
orderId: soId,
|
||||||
paymentDate: new Date(),
|
paymentDate: new Date(),
|
||||||
currencyIsoCode: fromOrder.currency_code,
|
currencyIsoCode: fromOrder.currency_code.toUpperCase(),
|
||||||
amountPaid: await this.totalsService_.getTotal(fromOrder),
|
amountPaid: this.bpnum_(fromOrder.total),
|
||||||
paymentType,
|
paymentType,
|
||||||
}
|
}
|
||||||
|
|
||||||
await client.payments.create(payment)
|
return client.payments.create(payment)
|
||||||
}
|
}
|
||||||
|
|
||||||
async getBrightpearlRows(fromOrder) {
|
async getBrightpearlRows(fromOrder) {
|
||||||
const region = await this.regionService_.retrieve(fromOrder.region_id)
|
const { region } = fromOrder
|
||||||
const discount = fromOrder.discounts.find(
|
const discount = fromOrder.discounts.find(
|
||||||
({ discount_rule }) => discount_rule.type !== "free_shipping"
|
({ rule }) => rule.type !== "free_shipping"
|
||||||
)
|
)
|
||||||
let lineDiscounts = []
|
let lineDiscounts = []
|
||||||
if (discount && !discount.is_giftcard) {
|
if (discount) {
|
||||||
lineDiscounts = this.totalsService_.getLineDiscounts(fromOrder, discount)
|
lineDiscounts = this.totalsService_.getLineDiscounts(fromOrder, discount)
|
||||||
}
|
}
|
||||||
|
|
||||||
const lines = await Promise.all(
|
const lines = await Promise.all(
|
||||||
fromOrder.items.map(async (item) => {
|
fromOrder.items.map(async (item) => {
|
||||||
const bpProduct = await this.retrieveProductBySKU(
|
const bpProduct = await this.retrieveProductBySKU(item.variant.sku)
|
||||||
item.content.variant.sku
|
|
||||||
)
|
|
||||||
|
|
||||||
const ld = lineDiscounts.find((l) => item._id === l.item._id) || {
|
const ld = lineDiscounts.find((l) => item.id === l.item.id) || {
|
||||||
amount: 0,
|
amount: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -710,13 +741,14 @@ class BrightpearlService extends BaseService {
|
|||||||
} else {
|
} else {
|
||||||
row.name = item.title
|
row.name = item.title
|
||||||
}
|
}
|
||||||
row.net = this.totalsService_.rounded(
|
row.net = this.bpnum_(item.unit_price * item.quantity - ld.amount)
|
||||||
item.content.unit_price * item.quantity - ld.amount
|
row.tax = this.bpnum_(
|
||||||
|
item.unit_price * item.quantity - ld.amount,
|
||||||
|
fromOrder.tax_rate
|
||||||
)
|
)
|
||||||
row.tax = this.totalsService_.rounded(row.net * fromOrder.tax_rate)
|
|
||||||
row.quantity = item.quantity
|
row.quantity = item.quantity
|
||||||
row.taxCode = region.tax_code
|
row.taxCode = region.tax_code
|
||||||
row.externalRef = item._id
|
row.externalRef = item.id
|
||||||
row.nominalCode = this.options.sales_account_code || "4000"
|
row.nominalCode = this.options.sales_account_code || "4000"
|
||||||
|
|
||||||
if (item.is_giftcard) {
|
if (item.is_giftcard) {
|
||||||
@@ -731,30 +763,28 @@ class BrightpearlService extends BaseService {
|
|||||||
// correspondingly. This reduces the amount payable, while debiting the
|
// correspondingly. This reduces the amount payable, while debiting the
|
||||||
// gift card account that was previously credited, when the gift card was
|
// gift card account that was previously credited, when the gift card was
|
||||||
// purchased.
|
// purchased.
|
||||||
if (discount && discount.is_giftcard) {
|
const gcTotal = fromOrder.gift_card_total
|
||||||
const discountTotal = await this.totalsService_.getDiscountTotal(
|
if (gcTotal) {
|
||||||
fromOrder
|
|
||||||
)
|
|
||||||
lines.push({
|
lines.push({
|
||||||
name: `Gift Card: ${discount.code}`,
|
name: `Gift Card`,
|
||||||
net: -1 * discountTotal,
|
net: this.bpnum_(-1 * gcTotal),
|
||||||
tax: this.totalsService_.rounded(
|
tax: this.bpnum_(-1 * gcTotal, fromOrder.tax_rate),
|
||||||
-1 * discountTotal * fromOrder.tax_rate
|
|
||||||
),
|
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
taxCode: region.tax_code,
|
taxCode: region.tax_code,
|
||||||
nominalCode: this.options.gift_card_account_code || "4000",
|
nominalCode: this.options.gift_card_account_code || "4000",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const shippingTotal = this.totalsService_.getShippingTotal(fromOrder)
|
const shippingTotal =
|
||||||
|
fromOrder.shipping_total ||
|
||||||
|
this.totalsService_.getShippingTotal(fromOrder)
|
||||||
const shippingMethods = fromOrder.shipping_methods
|
const shippingMethods = fromOrder.shipping_methods
|
||||||
if (shippingMethods.length > 0) {
|
if (shippingMethods.length > 0) {
|
||||||
lines.push({
|
lines.push({
|
||||||
name: `Shipping: ${shippingMethods.map((m) => m.name).join(" + ")}`,
|
name: `Shipping: ${shippingMethods.map((m) => m.name).join(" + ")}`,
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
net: this.totalsService_.rounded(shippingTotal),
|
net: this.bpnum_(shippingTotal),
|
||||||
tax: this.totalsService_.rounded(shippingTotal * fromOrder.tax_rate),
|
tax: this.bpnum_(shippingTotal, fromOrder.tax_rate),
|
||||||
taxCode: region.tax_code,
|
taxCode: region.tax_code,
|
||||||
nominalCode: this.options.shipping_account_code || "4040",
|
nominalCode: this.options.shipping_account_code || "4040",
|
||||||
})
|
})
|
||||||
@@ -821,11 +851,10 @@ class BrightpearlService extends BaseService {
|
|||||||
.filter((i) => !!i)
|
.filter((i) => !!i)
|
||||||
|
|
||||||
// Orders with a concatenated externalReference are swap orders
|
// Orders with a concatenated externalReference are swap orders
|
||||||
const [orderId, swapId] = order.externalRef.split(".")
|
const [_, swapId] = order.externalRef.split(".")
|
||||||
|
|
||||||
if (swapId) {
|
if (swapId) {
|
||||||
const order = await this.orderService_.retrieve(orderId)
|
return this.swapService_.createFulfillment(swapId, {
|
||||||
return this.swapService_.createFulfillment(order, swapId, {
|
|
||||||
goods_out_note: id,
|
goods_out_note: id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -866,6 +895,11 @@ class BrightpearlService extends BaseService {
|
|||||||
|
|
||||||
return { contactId: customer }
|
return { contactId: customer }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bpnum_(number, taxRate = 100) {
|
||||||
|
const bpNumber = number / 100
|
||||||
|
return this.totalsService_.rounded(bpNumber * (taxRate / 100))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default BrightpearlService
|
export default BrightpearlService
|
||||||
@@ -3,18 +3,24 @@ class OrderSubscriber {
|
|||||||
eventBusService,
|
eventBusService,
|
||||||
orderService,
|
orderService,
|
||||||
swapService,
|
swapService,
|
||||||
|
returnService,
|
||||||
|
paymentProviderService,
|
||||||
brightpearlService,
|
brightpearlService,
|
||||||
|
fulfillmentService,
|
||||||
}) {
|
}) {
|
||||||
this.orderService_ = orderService
|
this.orderService_ = orderService
|
||||||
this.brightpearlService_ = brightpearlService
|
this.brightpearlService_ = brightpearlService
|
||||||
this.swapService_ = swapService
|
this.swapService_ = swapService
|
||||||
|
this.returnService_ = returnService
|
||||||
|
this.paymentProviderService_ = paymentProviderService
|
||||||
|
this.fulfillmentService_ = fulfillmentService
|
||||||
|
|
||||||
|
eventBusService.subscribe("order.placed", this.sendToBrightpearl)
|
||||||
|
|
||||||
eventBusService.subscribe("order.refund_created", this.registerRefund)
|
eventBusService.subscribe("order.refund_created", this.registerRefund)
|
||||||
|
|
||||||
eventBusService.subscribe("order.items_returned", this.registerReturn)
|
eventBusService.subscribe("order.items_returned", this.registerReturn)
|
||||||
|
|
||||||
eventBusService.subscribe("order.placed", this.sendToBrightpearl)
|
|
||||||
|
|
||||||
eventBusService.subscribe(
|
eventBusService.subscribe(
|
||||||
"order.payment_captured",
|
"order.payment_captured",
|
||||||
this.registerCapturedPayment
|
this.registerCapturedPayment
|
||||||
@@ -24,50 +30,65 @@ class OrderSubscriber {
|
|||||||
eventBusService.subscribe("swap.shipment_created", this.registerShipment)
|
eventBusService.subscribe("swap.shipment_created", this.registerShipment)
|
||||||
|
|
||||||
// Before we initiate a swap we wait for the payment and the return
|
// Before we initiate a swap we wait for the payment and the return
|
||||||
eventBusService.subscribe("swap.payment_completed", this.registerSwap)
|
eventBusService.subscribe(
|
||||||
|
"swap.payment_completed",
|
||||||
|
this.registerSwapPayment
|
||||||
|
)
|
||||||
eventBusService.subscribe("order.swap_received", this.registerSwap)
|
eventBusService.subscribe("order.swap_received", this.registerSwap)
|
||||||
}
|
}
|
||||||
|
|
||||||
sendToBrightpearl = (order) => {
|
sendToBrightpearl = (data) => {
|
||||||
return this.brightpearlService_.createSalesOrder(order)
|
return this.brightpearlService_.createSalesOrder(data.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
registerCapturedPayment = (order) => {
|
registerCapturedPayment = ({ id }) => {
|
||||||
return this.brightpearlService_.createPayment(order)
|
return this.brightpearlService_.createPayment(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
registerSwapPayment = async (data) => {
|
||||||
|
return this.registerSwap({ id: data.id, swap_id: data.id })
|
||||||
}
|
}
|
||||||
|
|
||||||
registerSwap = async (data) => {
|
registerSwap = async (data) => {
|
||||||
const { order, swap, swap_id } = data
|
const { id, swap_id } = data
|
||||||
|
|
||||||
if (!order && !swap) {
|
if (!id && !swap_id) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let fromOrder = order
|
const fromSwap = await this.swapService_.retrieve(swap_id, {
|
||||||
if (!fromOrder) {
|
relations: [
|
||||||
fromOrder = await this.orderService_.retrieve(swap.order_id)
|
"order",
|
||||||
}
|
"order.payments",
|
||||||
|
"order.region",
|
||||||
|
"order.swaps",
|
||||||
|
"order.discounts",
|
||||||
|
"return_order",
|
||||||
|
"return_order.items",
|
||||||
|
"return_order.shipping_method",
|
||||||
|
"additional_items",
|
||||||
|
"shipping_address",
|
||||||
|
"shipping_methods",
|
||||||
|
],
|
||||||
|
})
|
||||||
|
let fromOrder = fromSwap.order
|
||||||
|
|
||||||
let fromSwap
|
if (
|
||||||
if (swap) {
|
!(
|
||||||
fromSwap = await this.swapService_.retrieve(swap._id)
|
fromSwap.confirmed_at !== null &&
|
||||||
} else {
|
fromSwap.return_order.status === "received"
|
||||||
fromSwap = await this.swapService_.retrieve(swap_id)
|
)
|
||||||
}
|
) {
|
||||||
|
|
||||||
if (!(fromSwap.is_paid && fromSwap.return.status === "received")) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.brightpearlService_.createSwapCredit(fromOrder, fromSwap)
|
await this.brightpearlService_.createSwapCredit(fromOrder, fromSwap)
|
||||||
await this.brightpearlService_.createSwapOrder(fromOrder, fromSwap)
|
await this.brightpearlService_.createSwapOrder(fromOrder, fromSwap)
|
||||||
|
|
||||||
const paySwap = await this.swapService_.retrieve(fromSwap._id)
|
|
||||||
await this.brightpearlService_.createSwapPayment(paySwap)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
registerShipment = async (data) => {
|
registerShipment = async (data) => {
|
||||||
const { shipment } = data
|
const { fulfillment_id } = data
|
||||||
|
const shipment = await this.fulfillmentService_.retrieve(fulfillment_id)
|
||||||
const noteId = shipment.metadata.goods_out_note
|
const noteId = shipment.metadata.goods_out_note
|
||||||
if (noteId) {
|
if (noteId) {
|
||||||
await this.brightpearlService_.registerGoodsOutTrackingNumber(
|
await this.brightpearlService_.registerGoodsOutTrackingNumber(
|
||||||
@@ -78,15 +99,28 @@ class OrderSubscriber {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
registerReturn = (data) => {
|
registerReturn = async (data) => {
|
||||||
const { order, return: fromReturn } = data
|
const { id, return_id } = data
|
||||||
|
|
||||||
|
const order = await this.orderService_.retrieve(id, {
|
||||||
|
relations: ["region", "payments"],
|
||||||
|
})
|
||||||
|
|
||||||
|
const fromReturn = await this.returnService_.retrieve(return_id, {
|
||||||
|
relations: ["items"],
|
||||||
|
})
|
||||||
|
|
||||||
return this.brightpearlService_
|
return this.brightpearlService_
|
||||||
.createSalesCredit(order, fromReturn)
|
.createSalesCredit(order, fromReturn)
|
||||||
.catch((err) => console.log(err))
|
.catch((err) => console.log(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
registerRefund = (data) => {
|
registerRefund = async (data) => {
|
||||||
const { order, refund } = data
|
const { id, refund_id } = data
|
||||||
|
const order = await this.orderService_.retrieve(id, {
|
||||||
|
relations: ["region", "payments"],
|
||||||
|
})
|
||||||
|
const refund = await this.paymentProviderService_.retrieveRefund(refund_id)
|
||||||
return this.brightpearlService_
|
return this.brightpearlService_
|
||||||
.createRefundCredit(order, refund)
|
.createRefundCredit(order, refund)
|
||||||
.catch((err) => console.log(err))
|
.catch((err) => console.log(err))
|
||||||
|
|||||||
@@ -5,5 +5,9 @@ node_modules
|
|||||||
/*.js
|
/*.js
|
||||||
!index.js
|
!index.js
|
||||||
yarn.lock
|
yarn.lock
|
||||||
|
src
|
||||||
|
.gitignore
|
||||||
|
.eslintrc
|
||||||
|
.babelrc
|
||||||
|
.prettierrc
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-plugin-contentful",
|
"name": "medusa-plugin-contentful",
|
||||||
"version": "1.0.15",
|
"version": "1.0.12-alpha.176+0646bd3",
|
||||||
"description": "Contentful plugin for Medusa Commerce",
|
"description": "Contentful plugin for Medusa Commerce",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -39,9 +39,9 @@
|
|||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"contentful-management": "^5.27.1",
|
"contentful-management": "^5.27.1",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"medusa-core-utils": "^1.0.11",
|
"medusa-core-utils": "^1.0.12-alpha.787+0646bd3",
|
||||||
"medusa-test-utils": "^1.0.13",
|
"medusa-test-utils": "^1.0.12-alpha.176+0646bd3",
|
||||||
"redis": "^3.0.2"
|
"redis": "^3.0.2"
|
||||||
},
|
},
|
||||||
"gitHead": "3cc7cbe5124cbcbb75f6e1435db4dcfaa2a60408"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -46,21 +46,16 @@ class ContentfulService extends BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getVariantEntries_(productId) {
|
async getVariantEntries_(variants) {
|
||||||
try {
|
try {
|
||||||
const productVariants = await this.productService_.retrieveVariants(
|
|
||||||
productId
|
|
||||||
)
|
|
||||||
|
|
||||||
const contentfulVariants = await Promise.all(
|
const contentfulVariants = await Promise.all(
|
||||||
productVariants.map((variant) =>
|
variants.map((variant) =>
|
||||||
this.updateProductVariantInContentful(variant)
|
this.updateProductVariantInContentful(variant)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return contentfulVariants
|
return contentfulVariants
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,79 +113,33 @@ class ContentfulService extends BaseService {
|
|||||||
|
|
||||||
async createProductInContentful(product) {
|
async createProductInContentful(product) {
|
||||||
try {
|
try {
|
||||||
|
const p = await this.productService_.retrieve(product.id, {
|
||||||
|
relations: ["variants", "options"],
|
||||||
|
})
|
||||||
|
|
||||||
const environment = await this.getContentfulEnvironment_()
|
const environment = await this.getContentfulEnvironment_()
|
||||||
const variantEntries = await this.getVariantEntries_(product._id)
|
const variantEntries = await this.getVariantEntries_(p.variants)
|
||||||
const variantLinks = this.getVariantLinks_(variantEntries)
|
const variantLinks = this.getVariantLinks_(variantEntries)
|
||||||
|
|
||||||
const fieldsObject = {
|
const result = await environment.createEntryWithId("product", p.id, {
|
||||||
title: {
|
fields: {
|
||||||
"en-US": product.title,
|
title: {
|
||||||
},
|
"en-US": p.title,
|
||||||
variants: {
|
|
||||||
"en-US": variantLinks,
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
"en-US": product.options,
|
|
||||||
},
|
|
||||||
objectId: {
|
|
||||||
"en-US": product._id,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if (product.images.length > 0) {
|
|
||||||
const imageLinks = await this.createImageAssets(product)
|
|
||||||
|
|
||||||
const thumbnailAsset = await environment.createAsset({
|
|
||||||
fields: {
|
|
||||||
title: {
|
|
||||||
"en-US": `${product.title}`,
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
"en-US": "",
|
|
||||||
},
|
|
||||||
file: {
|
|
||||||
"en-US": {
|
|
||||||
contentType: "image/xyz",
|
|
||||||
fileName: product.thumbnail,
|
|
||||||
upload: product.thumbnail,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
variants: {
|
||||||
|
"en-US": variantLinks,
|
||||||
await thumbnailAsset.processForAllLocales()
|
|
||||||
|
|
||||||
const thumbnailLink = {
|
|
||||||
sys: {
|
|
||||||
type: "Link",
|
|
||||||
linkType: "Asset",
|
|
||||||
id: thumbnailAsset.sys.id,
|
|
||||||
},
|
},
|
||||||
}
|
options: {
|
||||||
|
"en-US": p.options,
|
||||||
fieldsObject.thumbnail = {
|
|
||||||
"en-US": thumbnailLink,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (imageLinks) {
|
|
||||||
fieldsObject.images = {
|
|
||||||
"en-US": imageLinks,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await environment.createEntryWithId(
|
|
||||||
"product",
|
|
||||||
product._id,
|
|
||||||
{
|
|
||||||
fields: {
|
|
||||||
...fieldsObject,
|
|
||||||
},
|
},
|
||||||
}
|
objectId: {
|
||||||
)
|
"en-US": p.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
const ignoreIds = (await this.getIgnoreIds_("product")) || []
|
const ignoreIds = (await this.getIgnoreIds_("product")) || []
|
||||||
ignoreIds.push(product._id)
|
ignoreIds.push(product.id)
|
||||||
this.redis_.set("product_ignore_ids", JSON.stringify(ignoreIds))
|
this.redis_.set("product_ignore_ids", JSON.stringify(ignoreIds))
|
||||||
return result
|
return result
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -200,33 +149,37 @@ class ContentfulService extends BaseService {
|
|||||||
|
|
||||||
async createProductVariantInContentful(variant) {
|
async createProductVariantInContentful(variant) {
|
||||||
try {
|
try {
|
||||||
|
const v = await this.productVariantService_.retrieve(variant.id, {
|
||||||
|
relations: ["prices", "options"],
|
||||||
|
})
|
||||||
|
|
||||||
const environment = await this.getContentfulEnvironment_()
|
const environment = await this.getContentfulEnvironment_()
|
||||||
const result = await environment.createEntryWithId(
|
const result = await environment.createEntryWithId(
|
||||||
"productVariant",
|
"productVariant",
|
||||||
variant._id,
|
v.id,
|
||||||
{
|
{
|
||||||
fields: {
|
fields: {
|
||||||
title: {
|
title: {
|
||||||
"en-US": variant.title,
|
"en-US": v.title,
|
||||||
},
|
},
|
||||||
sku: {
|
sku: {
|
||||||
"en-US": variant.sku,
|
"en-US": v.sku,
|
||||||
},
|
},
|
||||||
prices: {
|
prices: {
|
||||||
"en-US": variant.prices,
|
"en-US": v.prices,
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
"en-US": variant.options,
|
"en-US": v.options,
|
||||||
},
|
},
|
||||||
objectId: {
|
objectId: {
|
||||||
"en-US": variant._id,
|
"en-US": v.id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const ignoreIds = (await this.getIgnoreIds_("product_variant")) || []
|
const ignoreIds = (await this.getIgnoreIds_("product_variant")) || []
|
||||||
ignoreIds.push(variant._id)
|
ignoreIds.push(v.id)
|
||||||
this.redis_.set("product_variant_ignore_ids", JSON.stringify(ignoreIds))
|
this.redis_.set("product_variant_ignore_ids", JSON.stringify(ignoreIds))
|
||||||
return result
|
return result
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -238,12 +191,12 @@ class ContentfulService extends BaseService {
|
|||||||
try {
|
try {
|
||||||
const ignoreIds = (await this.getIgnoreIds_("product")) || []
|
const ignoreIds = (await this.getIgnoreIds_("product")) || []
|
||||||
|
|
||||||
if (ignoreIds.includes(product._id)) {
|
if (ignoreIds.includes(product.id)) {
|
||||||
const newIgnoreIds = ignoreIds.filter((id) => id !== product._id)
|
const newIgnoreIds = ignoreIds.filter((id) => id !== product.id)
|
||||||
this.redis_.set("product_ignore_ids", JSON.stringify(newIgnoreIds))
|
this.redis_.set("product_ignore_ids", JSON.stringify(newIgnoreIds))
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
ignoreIds.push(product._id)
|
ignoreIds.push(product.id)
|
||||||
this.redis_.set("product_ignore_ids", JSON.stringify(ignoreIds))
|
this.redis_.set("product_ignore_ids", JSON.stringify(ignoreIds))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,27 +204,31 @@ class ContentfulService extends BaseService {
|
|||||||
// check if product exists
|
// check if product exists
|
||||||
let productEntry = undefined
|
let productEntry = undefined
|
||||||
try {
|
try {
|
||||||
productEntry = await environment.getEntry(product._id)
|
productEntry = await environment.getEntry(product.id)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return this.createProductInContentful(product)
|
return this.createProductInContentful(product)
|
||||||
}
|
}
|
||||||
|
|
||||||
const variantEntries = await this.getVariantEntries_(product._id)
|
const p = await this.productService_.retrieve(product.id, {
|
||||||
|
relations: ["options", "variants"],
|
||||||
|
})
|
||||||
|
|
||||||
|
const variantEntries = await this.getVariantEntries_(p.variants)
|
||||||
const variantLinks = this.getVariantLinks_(variantEntries)
|
const variantLinks = this.getVariantLinks_(variantEntries)
|
||||||
|
|
||||||
const productEntryFields = {
|
const productEntryFields = {
|
||||||
...productEntry.fields,
|
...productEntry.fields,
|
||||||
title: {
|
title: {
|
||||||
"en-US": product.title,
|
"en-US": p.title,
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
"en-US": product.options,
|
"en-US": p.options,
|
||||||
},
|
},
|
||||||
variants: {
|
variants: {
|
||||||
"en-US": variantLinks,
|
"en-US": variantLinks,
|
||||||
},
|
},
|
||||||
objectId: {
|
objectId: {
|
||||||
"en-US": product._id,
|
"en-US": p.id,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,15 +247,15 @@ class ContentfulService extends BaseService {
|
|||||||
try {
|
try {
|
||||||
const ignoreIds = (await this.getIgnoreIds_("product_variant")) || []
|
const ignoreIds = (await this.getIgnoreIds_("product_variant")) || []
|
||||||
|
|
||||||
if (ignoreIds.includes(variant._id)) {
|
if (ignoreIds.includes(variant.id)) {
|
||||||
const newIgnoreIds = ignoreIds.filter((id) => id !== variant._id)
|
const newIgnoreIds = ignoreIds.filter((id) => id !== variant.id)
|
||||||
this.redis_.set(
|
this.redis_.set(
|
||||||
"product_variant_ignore_ids",
|
"product_variant_ignore_ids",
|
||||||
JSON.stringify(newIgnoreIds)
|
JSON.stringify(newIgnoreIds)
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
ignoreIds.push(variant._id)
|
ignoreIds.push(variant.id)
|
||||||
this.redis_.set("product_variant_ignore_ids", JSON.stringify(ignoreIds))
|
this.redis_.set("product_variant_ignore_ids", JSON.stringify(ignoreIds))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,32 +264,31 @@ class ContentfulService extends BaseService {
|
|||||||
let variantEntry = undefined
|
let variantEntry = undefined
|
||||||
// if not, we create a new one
|
// if not, we create a new one
|
||||||
try {
|
try {
|
||||||
variantEntry = await environment.getEntry(variant._id)
|
variantEntry = await environment.getEntry(variant.id)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return this.createProductVariantInContentful(variant)
|
return this.createProductVariantInContentful(variant)
|
||||||
}
|
}
|
||||||
|
|
||||||
const cleanPrices = variant.prices.map((price) => ({
|
const v = await this.productVariantService_.retrieve(variant.id, {
|
||||||
...price,
|
relations: ["prices", "options"],
|
||||||
sale_amount: price.sale_amount || undefined,
|
})
|
||||||
}))
|
|
||||||
|
|
||||||
const variantEntryFields = {
|
const variantEntryFields = {
|
||||||
...variantEntry.fields,
|
...variantEntry.fields,
|
||||||
title: {
|
title: {
|
||||||
"en-US": variant.title,
|
"en-US": v.title,
|
||||||
},
|
},
|
||||||
sku: {
|
sku: {
|
||||||
"en-US": variant.sku,
|
"en-US": v.sku,
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
"en-US": variant.options,
|
"en-US": v.options,
|
||||||
},
|
},
|
||||||
prices: {
|
prices: {
|
||||||
"en-US": cleanPrices,
|
"en-US": v.prices,
|
||||||
},
|
},
|
||||||
objectId: {
|
objectId: {
|
||||||
"en-US": variant._id,
|
"en-US": v.id,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
class ContentfulSubscriber {
|
class ContentfulSubscriber {
|
||||||
constructor({ contentfulService, eventBusService }) {
|
constructor({
|
||||||
|
contentfulService,
|
||||||
|
productVariantService,
|
||||||
|
productService,
|
||||||
|
eventBusService,
|
||||||
|
}) {
|
||||||
|
this.productVariantService_ = productVariantService
|
||||||
|
this.productService_ = productService
|
||||||
this.contentfulService_ = contentfulService
|
this.contentfulService_ = contentfulService
|
||||||
this.eventBus_ = eventBusService
|
this.eventBus_ = eventBusService
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-plugin-discount-generator",
|
"name": "medusa-plugin-discount-generator",
|
||||||
"version": "1.0.1",
|
"version": "1.0.2-alpha.787+0646bd3",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"author": "Sebastian Rindom",
|
"author": "Sebastian Rindom",
|
||||||
@@ -40,5 +40,5 @@
|
|||||||
"medusa-core-utils": "^0.1.27",
|
"medusa-core-utils": "^0.1.27",
|
||||||
"randomatic": "^3.1.1"
|
"randomatic": "^3.1.1"
|
||||||
},
|
},
|
||||||
"gitHead": "3cc7cbe5124cbcbb75f6e1435db4dcfaa2a60408"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-plugin-economic",
|
"name": "medusa-plugin-economic",
|
||||||
"version": "1.0.13",
|
"version": "1.0.12-alpha.176+0646bd3",
|
||||||
"description": "E-conomic financial reporting",
|
"description": "E-conomic financial reporting",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -39,9 +39,9 @@
|
|||||||
"axios": "^0.19.2",
|
"axios": "^0.19.2",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"medusa-core-utils": "^1.0.11",
|
"medusa-core-utils": "^1.0.12-alpha.787+0646bd3",
|
||||||
"medusa-test-utils": "^1.0.13",
|
"medusa-test-utils": "^1.0.12-alpha.176+0646bd3",
|
||||||
"moment": "^2.27.0"
|
"moment": "^2.27.0"
|
||||||
},
|
},
|
||||||
"gitHead": "3cc7cbe5124cbcbb75f6e1435db4dcfaa2a60408"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -71,7 +71,7 @@ class EconomicService extends BaseService {
|
|||||||
let order_lines = []
|
let order_lines = []
|
||||||
// Find the discount, that is not free shipping
|
// Find the discount, that is not free shipping
|
||||||
const discount = order.discounts.find(
|
const discount = order.discounts.find(
|
||||||
({ discount_rule }) => discount_rule.type !== "free_shipping"
|
({ rule }) => rule.type !== "free_shipping"
|
||||||
)
|
)
|
||||||
// If the discount has an item specific allocation method,
|
// If the discount has an item specific allocation method,
|
||||||
// we need to fetch the discount for each item
|
// we need to fetch the discount for each item
|
||||||
|
|||||||
@@ -5,5 +5,9 @@ node_modules
|
|||||||
/*.js
|
/*.js
|
||||||
!index.js
|
!index.js
|
||||||
yarn.lock
|
yarn.lock
|
||||||
|
src
|
||||||
|
.gitignore
|
||||||
|
.eslintrc
|
||||||
|
.babelrc
|
||||||
|
.prettierrc
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-plugin-ip-lookup",
|
"name": "medusa-plugin-ip-lookup",
|
||||||
"version": "1.0.11",
|
"version": "1.0.12-alpha.787+0646bd3",
|
||||||
"description": "IP lookup middleware for Medusa core",
|
"description": "IP lookup middleware for Medusa core",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -35,5 +35,5 @@
|
|||||||
"axios": "^0.20.0",
|
"axios": "^0.20.0",
|
||||||
"mongoose": "^5.8.0"
|
"mongoose": "^5.8.0"
|
||||||
},
|
},
|
||||||
"gitHead": "3cc7cbe5124cbcbb75f6e1435db4dcfaa2a60408"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,8 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ipLookupService = req.scope.resolve("ipLookupService")
|
const ipLookupService = req.scope.resolve("ipLookupService")
|
||||||
const regionService = req.scope.resolve("regionService")
|
const manager = req.scope.resolve("manager")
|
||||||
|
const countryRepository = req.scope.resolve("countryRepository")
|
||||||
|
|
||||||
const ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress
|
const ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress
|
||||||
|
|
||||||
@@ -18,15 +19,15 @@ export default {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find region using the country code from ip lookup
|
const countryRepo = manager.getCustomRepository(countryRepository)
|
||||||
const regions = await regionService.list({
|
const country = await countryRepo.findOne({
|
||||||
countries: data.country_code,
|
where: { iso_2: data.country_code.toLowerCase() },
|
||||||
})
|
})
|
||||||
|
|
||||||
// If this region exists, add it to the body of the cart creation request
|
// If country exists, add it to the body of the cart creation request
|
||||||
if (regions[0]) {
|
if (country) {
|
||||||
req.body.region_id = regions[0]._id.toString()
|
req.body.region_id = country.region_id
|
||||||
req.body.country_code = data.country_code
|
req.body.country_code = country.iso_2
|
||||||
}
|
}
|
||||||
|
|
||||||
next()
|
next()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-plugin-mailchimp",
|
"name": "medusa-plugin-mailchimp",
|
||||||
"version": "1.0.15",
|
"version": "1.0.14-alpha.176+0646bd3",
|
||||||
"description": "Mailchimp newsletter subscriptions",
|
"description": "Mailchimp newsletter subscriptions",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -40,7 +40,8 @@
|
|||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"mailchimp-api-v3": "^1.14.0",
|
"mailchimp-api-v3": "^1.14.0",
|
||||||
"medusa-core-utils": "^1.0.11",
|
"medusa-core-utils": "^1.0.12-alpha.787+0646bd3",
|
||||||
"medusa-test-utils": "^1.0.13"
|
"medusa-test-utils": "^1.0.12-alpha.176+0646bd3"
|
||||||
}
|
},
|
||||||
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-plugin-permissions",
|
"name": "medusa-plugin-permissions",
|
||||||
"version": "1.0.13",
|
"version": "1.0.12-alpha.176+0646bd3",
|
||||||
"description": "Role permission for Medusa core",
|
"description": "Role permission for Medusa core",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -32,9 +32,9 @@
|
|||||||
"medusa-interfaces": "1.x"
|
"medusa-interfaces": "1.x"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"medusa-core-utils": "^1.0.11",
|
"medusa-core-utils": "^1.0.12-alpha.787+0646bd3",
|
||||||
"medusa-test-utils": "^1.0.13",
|
"medusa-test-utils": "^1.0.12-alpha.176+0646bd3",
|
||||||
"mongoose": "^5.8.0"
|
"mongoose": "^5.8.0"
|
||||||
},
|
},
|
||||||
"gitHead": "3cc7cbe5124cbcbb75f6e1435db4dcfaa2a60408"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-plugin-segment",
|
"name": "medusa-plugin-segment",
|
||||||
"version": "1.0.16",
|
"version": "1.0.14-alpha.176+0646bd3",
|
||||||
"description": "Segment Analytics",
|
"description": "Segment Analytics",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -39,8 +39,8 @@
|
|||||||
"axios": "^0.19.2",
|
"axios": "^0.19.2",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"medusa-core-utils": "^1.0.11",
|
"medusa-core-utils": "^1.0.12-alpha.787+0646bd3",
|
||||||
"medusa-test-utils": "^1.0.13"
|
"medusa-test-utils": "^1.0.12-alpha.176+0646bd3"
|
||||||
},
|
},
|
||||||
"gitHead": "3cc7cbe5124cbcbb75f6e1435db4dcfaa2a60408"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -13,8 +13,8 @@ class SegmentService extends BaseService {
|
|||||||
constructor({ totalsService }, options) {
|
constructor({ totalsService }, options) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
this.options_ = options
|
|
||||||
this.totalsService_ = totalsService
|
this.totalsService_ = totalsService
|
||||||
|
this.options_ = options
|
||||||
|
|
||||||
this.analytics_ = new Analytics(options.write_key)
|
this.analytics_ = new Analytics(options.write_key)
|
||||||
}
|
}
|
||||||
@@ -30,14 +30,17 @@ class SegmentService extends BaseService {
|
|||||||
return this.analytics_.track(data)
|
return this.analytics_.track(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
async getReportingValue(fromCurrency, value) {
|
async getReportingValue(rawCurrency, value) {
|
||||||
|
const fromCurrency = rawCurrency.toUpperCase()
|
||||||
const date = "latest"
|
const date = "latest"
|
||||||
const toCurrency =
|
const toCurrency =
|
||||||
(this.options_.reporting_currency &&
|
(this.options_.reporting_currency &&
|
||||||
this.options_.reporting_currency.toUpperCase()) ||
|
this.options_.reporting_currency.toUpperCase()) ||
|
||||||
"EUR"
|
"EUR"
|
||||||
|
|
||||||
if (fromCurrency === toCurrency) return value.toFixed(2)
|
if (fromCurrency === toCurrency) {
|
||||||
|
return this.totalsService_.rounded(value)
|
||||||
|
}
|
||||||
|
|
||||||
const exchangeRate = await axios
|
const exchangeRate = await axios
|
||||||
.get(
|
.get(
|
||||||
@@ -47,28 +50,28 @@ class SegmentService extends BaseService {
|
|||||||
return data.rates[fromCurrency]
|
return data.rates[fromCurrency]
|
||||||
})
|
})
|
||||||
|
|
||||||
return (value / exchangeRate).toFixed(2)
|
return this.totalsService_.rounded(value / exchangeRate)
|
||||||
}
|
}
|
||||||
|
|
||||||
async buildOrder(order) {
|
async buildOrder(order) {
|
||||||
const subtotal = await this.totalsService_.getSubtotal(order)
|
const subtotal = order.subtotal / 100
|
||||||
const total = await this.totalsService_.getTotal(order)
|
const total = order.total / 100
|
||||||
const tax = await this.totalsService_.getTaxTotal(order)
|
const tax = order.tax_total / 100
|
||||||
const discount = await this.totalsService_.getDiscountTotal(order)
|
const discount = order.discount_total / 100
|
||||||
const shipping = await this.totalsService_.getShippingTotal(order)
|
const shipping = order.shipping_total / 100
|
||||||
const revenue = total - tax
|
const revenue = total - tax
|
||||||
|
|
||||||
let coupon
|
let coupon
|
||||||
if (order.discounts && order.discounts.length) {
|
if (order.discounts && order.discounts.length) {
|
||||||
coupon = order.discounts[0].code
|
coupon = order.discounts[0] && order.discounts[0].code
|
||||||
}
|
}
|
||||||
|
|
||||||
const orderData = {
|
const orderData = {
|
||||||
checkout_id: order.cart_id,
|
checkout_id: order.cart_id,
|
||||||
order_id: order._id,
|
order_id: order.id,
|
||||||
email: order.email,
|
email: order.email,
|
||||||
region_id: order.region_id,
|
region_id: order.region_id,
|
||||||
payment_provider: order.payment_method.provider_id,
|
payment_provider: order.payments.map((p) => p.provider_id).join(","),
|
||||||
shipping_methods: order.shipping_methods,
|
shipping_methods: order.shipping_methods,
|
||||||
shipping_country: order.shipping_address.country_code,
|
shipping_country: order.shipping_address.country_code,
|
||||||
shipping_city: order.shipping_address.city,
|
shipping_city: order.shipping_address.city,
|
||||||
@@ -102,8 +105,8 @@ class SegmentService extends BaseService {
|
|||||||
order.items.map(async (item) => {
|
order.items.map(async (item) => {
|
||||||
let name = item.title
|
let name = item.title
|
||||||
|
|
||||||
const unit_price = item.content.unit_price
|
const unit_price = item.unit_price
|
||||||
const line_total = unit_price * item.content.quantity * item.quantity
|
const line_total = (unit_price * item.quantity) / 100
|
||||||
const revenue = await this.getReportingValue(
|
const revenue = await this.getReportingValue(
|
||||||
order.currency_code,
|
order.currency_code,
|
||||||
line_total
|
line_total
|
||||||
@@ -114,11 +117,11 @@ class SegmentService extends BaseService {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
variant: item.content.variant.sku,
|
variant,
|
||||||
price: unit_price,
|
price: unit_price / 100,
|
||||||
reporting_revenue: revenue,
|
reporting_revenue: revenue,
|
||||||
product_id: `${item.content.product._id}`,
|
product_id: item.variant.product_id,
|
||||||
sku: skuParts.join("-"),
|
sku: item.variant.sku,
|
||||||
quantity: item.quantity,
|
quantity: item.quantity,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,13 +1,55 @@
|
|||||||
class OrderSubscriber {
|
class OrderSubscriber {
|
||||||
constructor({ segmentService, eventBusService }) {
|
constructor({
|
||||||
|
segmentService,
|
||||||
|
eventBusService,
|
||||||
|
orderService,
|
||||||
|
returnService,
|
||||||
|
}) {
|
||||||
|
this.orderService_ = orderService
|
||||||
|
|
||||||
|
this.returnService_ = returnService
|
||||||
|
|
||||||
eventBusService.subscribe(
|
eventBusService.subscribe(
|
||||||
"order.items_returned",
|
"order.items_returned",
|
||||||
async ({ order, return: ret }) => {
|
async ({ id, return_id }) => {
|
||||||
|
const order = await this.orderService_.retrieve(id, {
|
||||||
|
select: [
|
||||||
|
"shipping_total",
|
||||||
|
"discount_total",
|
||||||
|
"tax_total",
|
||||||
|
"refunded_total",
|
||||||
|
"gift_card_total",
|
||||||
|
"subtotal",
|
||||||
|
"total",
|
||||||
|
],
|
||||||
|
relations: [
|
||||||
|
"customer",
|
||||||
|
"billing_address",
|
||||||
|
"shipping_address",
|
||||||
|
"discounts",
|
||||||
|
"shipping_methods",
|
||||||
|
"payments",
|
||||||
|
"fulfillments",
|
||||||
|
"returns",
|
||||||
|
"gift_cards",
|
||||||
|
"gift_card_transactions",
|
||||||
|
"swaps",
|
||||||
|
"swaps.return_order",
|
||||||
|
"swaps.payment",
|
||||||
|
"swaps.shipping_methods",
|
||||||
|
"swaps.shipping_address",
|
||||||
|
"swaps.additional_items",
|
||||||
|
"swaps.fulfillments",
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const ret = await this.returnService_.retrieve(return_id)
|
||||||
|
|
||||||
const shipping = []
|
const shipping = []
|
||||||
if (ret.shipping_method && ret.shipping_method.price) {
|
if (ret.shipping_method && ret.shipping_method.price) {
|
||||||
shipping.push({
|
shipping.push({
|
||||||
...ret.shipping_method,
|
...ret.shipping_method,
|
||||||
price: -1 * ret.shipping_method.price,
|
price: -1 * (ret.shipping_method.price / 100),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15,7 +57,7 @@ class OrderSubscriber {
|
|||||||
...order,
|
...order,
|
||||||
shipping_methods: shipping,
|
shipping_methods: shipping,
|
||||||
items: ret.items.map((i) =>
|
items: ret.items.map((i) =>
|
||||||
order.items.find((l) => l._id === i.item_id)
|
order.items.find((l) => l.id === i.item_id)
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,7 +73,19 @@ class OrderSubscriber {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
eventBusService.subscribe("order.canceled", async (order) => {
|
eventBusService.subscribe("order.canceled", async ({ id }) => {
|
||||||
|
const order = await this.orderService_.retrieve(id, {
|
||||||
|
select: [
|
||||||
|
"shipping_total",
|
||||||
|
"discount_total",
|
||||||
|
"tax_total",
|
||||||
|
"refunded_total",
|
||||||
|
"gift_card_total",
|
||||||
|
"subtotal",
|
||||||
|
"total",
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
const date = new Date()
|
const date = new Date()
|
||||||
const orderData = await segmentService.buildOrder(order)
|
const orderData = await segmentService.buildOrder(order)
|
||||||
const orderEvent = {
|
const orderEvent = {
|
||||||
@@ -44,7 +98,38 @@ class OrderSubscriber {
|
|||||||
segmentService.track(orderEvent)
|
segmentService.track(orderEvent)
|
||||||
})
|
})
|
||||||
|
|
||||||
eventBusService.subscribe("order.placed", async (order) => {
|
eventBusService.subscribe("order.placed", async ({ id }) => {
|
||||||
|
const order = await this.orderService_.retrieve(id, {
|
||||||
|
select: [
|
||||||
|
"shipping_total",
|
||||||
|
"discount_total",
|
||||||
|
"tax_total",
|
||||||
|
"refunded_total",
|
||||||
|
"gift_card_total",
|
||||||
|
"subtotal",
|
||||||
|
"total",
|
||||||
|
],
|
||||||
|
relations: [
|
||||||
|
"customer",
|
||||||
|
"billing_address",
|
||||||
|
"shipping_address",
|
||||||
|
"discounts",
|
||||||
|
"shipping_methods",
|
||||||
|
"payments",
|
||||||
|
"fulfillments",
|
||||||
|
"returns",
|
||||||
|
"gift_cards",
|
||||||
|
"gift_card_transactions",
|
||||||
|
"swaps",
|
||||||
|
"swaps.return_order",
|
||||||
|
"swaps.payment",
|
||||||
|
"swaps.shipping_methods",
|
||||||
|
"swaps.shipping_address",
|
||||||
|
"swaps.additional_items",
|
||||||
|
"swaps.fulfillments",
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
const date = new Date(parseInt(order.created))
|
const date = new Date(parseInt(order.created))
|
||||||
const orderData = await segmentService.buildOrder(order)
|
const orderData = await segmentService.buildOrder(order)
|
||||||
const orderEvent = {
|
const orderEvent = {
|
||||||
|
|||||||
@@ -5,5 +5,9 @@ node_modules
|
|||||||
/*.js
|
/*.js
|
||||||
!index.js
|
!index.js
|
||||||
yarn.lock
|
yarn.lock
|
||||||
|
src
|
||||||
|
.gitignore
|
||||||
|
.eslintrc
|
||||||
|
.babelrc
|
||||||
|
.prettierrc
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-plugin-sendgrid",
|
"name": "medusa-plugin-sendgrid",
|
||||||
"version": "1.0.14",
|
"version": "1.0.13-alpha.176+0646bd3",
|
||||||
"description": "SendGrid transactional emails",
|
"description": "SendGrid transactional emails",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -39,8 +39,8 @@
|
|||||||
"@sendgrid/mail": "^7.1.1",
|
"@sendgrid/mail": "^7.1.1",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"medusa-core-utils": "^1.0.11",
|
"medusa-core-utils": "^1.0.12-alpha.787+0646bd3",
|
||||||
"medusa-test-utils": "^1.0.13"
|
"medusa-test-utils": "^1.0.12-alpha.176+0646bd3"
|
||||||
},
|
},
|
||||||
"gitHead": "3cc7cbe5124cbcbb75f6e1435db4dcfaa2a60408"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -37,8 +37,7 @@ class SendGridService extends BaseService {
|
|||||||
templateId = this.options_.gift_card_created_template
|
templateId = this.options_.gift_card_created_template
|
||||||
data = {
|
data = {
|
||||||
...data,
|
...data,
|
||||||
display_value:
|
display_value: data.giftcard.rule.value * (1 + data.tax_rate),
|
||||||
data.giftcard.discount_rule.value * (1 + data.tax_rate),
|
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case "order.placed":
|
case "order.placed":
|
||||||
|
|||||||
@@ -4,16 +4,52 @@ class OrderSubscriber {
|
|||||||
orderService,
|
orderService,
|
||||||
sendgridService,
|
sendgridService,
|
||||||
eventBusService,
|
eventBusService,
|
||||||
|
fulfillmentService,
|
||||||
}) {
|
}) {
|
||||||
this.orderService_ = orderService
|
this.orderService_ = orderService
|
||||||
this.totalsService_ = totalsService
|
this.totalsService_ = totalsService
|
||||||
this.sendgridService_ = sendgridService
|
this.sendgridService_ = sendgridService
|
||||||
this.eventBus_ = eventBusService
|
this.eventBus_ = eventBusService
|
||||||
|
this.fulfillmentService_ = fulfillmentService
|
||||||
|
|
||||||
this.eventBus_.subscribe(
|
this.eventBus_.subscribe(
|
||||||
"order.shipment_created",
|
"order.shipment_created",
|
||||||
async ({ order_id, shipment }) => {
|
async ({ id, fulfillment_id }) => {
|
||||||
const order = await this.orderService_.retrieve(order_id)
|
const order = await this.orderService_.retrieve(id, {
|
||||||
|
select: [
|
||||||
|
"shipping_total",
|
||||||
|
"discount_total",
|
||||||
|
"tax_total",
|
||||||
|
"refunded_total",
|
||||||
|
"gift_card_total",
|
||||||
|
"subtotal",
|
||||||
|
"total",
|
||||||
|
"refundable_amount",
|
||||||
|
],
|
||||||
|
relations: [
|
||||||
|
"customer",
|
||||||
|
"billing_address",
|
||||||
|
"shipping_address",
|
||||||
|
"discounts",
|
||||||
|
"shipping_methods",
|
||||||
|
"shipping_methods.shipping_option",
|
||||||
|
"payments",
|
||||||
|
"fulfillments",
|
||||||
|
"returns",
|
||||||
|
"gift_cards",
|
||||||
|
"gift_card_transactions",
|
||||||
|
"swaps",
|
||||||
|
"swaps.return_order",
|
||||||
|
"swaps.payment",
|
||||||
|
"swaps.shipping_methods",
|
||||||
|
"swaps.shipping_address",
|
||||||
|
"swaps.additional_items",
|
||||||
|
"swaps.fulfillments",
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const shipment = await this.fulfillmentService_.retrieve(fulfillment_id)
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
...order,
|
...order,
|
||||||
tracking_number: shipment.tracking_numbers.join(", "),
|
tracking_number: shipment.tracking_numbers.join(", "),
|
||||||
@@ -33,50 +69,108 @@ class OrderSubscriber {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
this.eventBus_.subscribe("order.placed", async (order) => {
|
this.eventBus_.subscribe("order.placed", async (orderObj) => {
|
||||||
const subtotal = await this.totalsService_.getSubtotal(order)
|
try {
|
||||||
const tax_total = await this.totalsService_.getTaxTotal(order)
|
const order = await this.orderService_.retrieve(orderObj.id, {
|
||||||
const discount_total = await this.totalsService_.getDiscountTotal(order)
|
select: [
|
||||||
const shipping_total = await this.totalsService_.getShippingTotal(order)
|
"shipping_total",
|
||||||
const total = await this.totalsService_.getTotal(order)
|
"discount_total",
|
||||||
|
"tax_total",
|
||||||
|
"refunded_total",
|
||||||
|
"gift_card_total",
|
||||||
|
"subtotal",
|
||||||
|
"total",
|
||||||
|
],
|
||||||
|
relations: [
|
||||||
|
"customer",
|
||||||
|
"billing_address",
|
||||||
|
"shipping_address",
|
||||||
|
"discounts",
|
||||||
|
"shipping_methods",
|
||||||
|
"shipping_methods.shipping_option",
|
||||||
|
"payments",
|
||||||
|
"fulfillments",
|
||||||
|
"returns",
|
||||||
|
"gift_cards",
|
||||||
|
"gift_card_transactions",
|
||||||
|
"swaps",
|
||||||
|
"swaps.return_order",
|
||||||
|
"swaps.payment",
|
||||||
|
"swaps.shipping_methods",
|
||||||
|
"swaps.shipping_address",
|
||||||
|
"swaps.additional_items",
|
||||||
|
"swaps.fulfillments",
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
const date = new Date(parseInt(order.created))
|
const {
|
||||||
const data = {
|
subtotal,
|
||||||
...order,
|
tax_total,
|
||||||
date: date.toDateString(),
|
discount_total,
|
||||||
items: order.items.map((i) => {
|
shipping_total,
|
||||||
|
total,
|
||||||
|
} = order
|
||||||
|
|
||||||
|
const taxRate = order.tax_rate / 100
|
||||||
|
const currencyCode = order.currency_code.toUpperCase()
|
||||||
|
|
||||||
|
const items = order.items.map((i) => {
|
||||||
return {
|
return {
|
||||||
...i,
|
...i,
|
||||||
price: `${(i.content.unit_price * (1 + order.tax_rate)).toFixed(
|
price: `${((i.unit_price / 100) * (1 + taxRate)).toFixed(
|
||||||
2
|
2
|
||||||
)} ${order.currency_code}`,
|
)} ${currencyCode}`,
|
||||||
}
|
}
|
||||||
}),
|
})
|
||||||
discounts: order.discounts.map((discount) => {
|
|
||||||
return {
|
|
||||||
is_giftcard: discount.is_giftcard,
|
|
||||||
code: discount.code,
|
|
||||||
descriptor: `${discount.discount_rule.value}${
|
|
||||||
discount.discount_rule.type === "percentage"
|
|
||||||
? "%"
|
|
||||||
: ` ${order.currency_code}`
|
|
||||||
}`,
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
subtotal: `${(subtotal * (1 + order.tax_rate)).toFixed(2)} ${
|
|
||||||
order.currency_code
|
|
||||||
}`,
|
|
||||||
tax_total: `${tax_total.toFixed(2)} ${order.currency_code}`,
|
|
||||||
discount_total: `${(discount_total * (1 + order.tax_rate)).toFixed(
|
|
||||||
2
|
|
||||||
)} ${order.currency_code}`,
|
|
||||||
shipping_total: `${(shipping_total * (1 + order.tax_rate)).toFixed(
|
|
||||||
2
|
|
||||||
)} ${order.currency_code}`,
|
|
||||||
total: `${total.toFixed(2)} ${order.currency_code}`,
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.sendgridService_.transactionalEmail("order.placed", data)
|
let discounts = []
|
||||||
|
if (order.discounts) {
|
||||||
|
discounts = order.discounts.map((discount) => {
|
||||||
|
return {
|
||||||
|
is_giftcard: false,
|
||||||
|
code: discount.code,
|
||||||
|
descriptor: `${discount.rule.value}${
|
||||||
|
discount.rule.type === "percentage" ? "%" : ` ${currencyCode}`
|
||||||
|
}`,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let giftCards = []
|
||||||
|
if (order.gift_cards) {
|
||||||
|
giftCards = order.gift_cards.map((gc) => {
|
||||||
|
return {
|
||||||
|
is_giftcard: true,
|
||||||
|
code: gc.code,
|
||||||
|
descriptor: `${gc.value} ${currencyCode}`,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
discounts.concat(giftCards)
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
...order,
|
||||||
|
date: order.created_at.toDateString(),
|
||||||
|
items,
|
||||||
|
discounts,
|
||||||
|
subtotal: `${((subtotal / 100) * (1 + taxRate)).toFixed(
|
||||||
|
2
|
||||||
|
)} ${currencyCode}`,
|
||||||
|
tax_total: `${(tax_total / 100).toFixed(2)} ${currencyCode}`,
|
||||||
|
discount_total: `${((discount_total / 100) * (1 + taxRate)).toFixed(
|
||||||
|
2
|
||||||
|
)} ${currencyCode}`,
|
||||||
|
shipping_total: `${((shipping_total / 100) * (1 + taxRate)).toFixed(
|
||||||
|
2
|
||||||
|
)} ${currencyCode}`,
|
||||||
|
total: `${(total / 100).toFixed(2)} ${currencyCode}`,
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.sendgridService_.transactionalEmail("order.placed", data)
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.eventBus_.subscribe("order.cancelled", async (order) => {
|
this.eventBus_.subscribe("order.cancelled", async (order) => {
|
||||||
|
|||||||
@@ -5,5 +5,7 @@ node_modules
|
|||||||
/*.js
|
/*.js
|
||||||
!index.js
|
!index.js
|
||||||
yarn.lock
|
yarn.lock
|
||||||
|
src
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-plugin-slack-notification",
|
"name": "medusa-plugin-slack-notification",
|
||||||
"version": "1.0.13",
|
"version": "1.0.12-alpha.176+0646bd3",
|
||||||
"description": "Slack notifications",
|
"description": "Slack notifications",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -39,9 +39,9 @@
|
|||||||
"axios": "^0.19.2",
|
"axios": "^0.19.2",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"medusa-core-utils": "^1.0.11",
|
"medusa-core-utils": "^1.0.12-alpha.787+0646bd3",
|
||||||
"medusa-test-utils": "^1.0.13",
|
"medusa-test-utils": "^1.0.12-alpha.176+0646bd3",
|
||||||
"moment": "^2.27.0"
|
"moment": "^2.27.0"
|
||||||
},
|
},
|
||||||
"gitHead": "3cc7cbe5124cbcbb75f6e1435db4dcfaa2a60408"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import axios from "axios"
|
import axios from "axios"
|
||||||
import moment from "moment"
|
|
||||||
import { BaseService } from "medusa-interfaces"
|
import { BaseService } from "medusa-interfaces"
|
||||||
|
|
||||||
class SlackService extends BaseService {
|
class SlackService extends BaseService {
|
||||||
@@ -23,20 +22,48 @@ class SlackService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async orderNotification(orderId) {
|
async orderNotification(orderId) {
|
||||||
const order = await this.orderService_.retrieve(orderId)
|
const order = await this.orderService_.retrieve(orderId, {
|
||||||
|
select: [
|
||||||
|
"shipping_total",
|
||||||
|
"discount_total",
|
||||||
|
"tax_total",
|
||||||
|
"refunded_total",
|
||||||
|
"gift_card_total",
|
||||||
|
"subtotal",
|
||||||
|
"total",
|
||||||
|
],
|
||||||
|
relations: [
|
||||||
|
"customer",
|
||||||
|
"billing_address",
|
||||||
|
"shipping_address",
|
||||||
|
"discounts",
|
||||||
|
"shipping_methods",
|
||||||
|
"payments",
|
||||||
|
"fulfillments",
|
||||||
|
"returns",
|
||||||
|
"gift_cards",
|
||||||
|
"gift_card_transactions",
|
||||||
|
"swaps",
|
||||||
|
"swaps.return_order",
|
||||||
|
"swaps.payment",
|
||||||
|
"swaps.shipping_methods",
|
||||||
|
"swaps.shipping_address",
|
||||||
|
"swaps.additional_items",
|
||||||
|
"swaps.fulfillments",
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
const subtotal = await this.totalsService_.getSubtotal(order)
|
const { subtotal, tax_total, discount_total, shipping_total, total } = order
|
||||||
const shippingTotal = await this.totalsService_.getShippingTotal(order)
|
|
||||||
const taxTotal = await this.totalsService_.getTaxTotal(order)
|
const currencyCode = order.currency_code.toUpperCase()
|
||||||
const discountTotal = await this.totalsService_.getDiscountTotal(order)
|
const taxRate = order.tax_rate / 100
|
||||||
const total = await this.totalsService_.getTotal(order)
|
|
||||||
|
|
||||||
let blocks = [
|
let blocks = [
|
||||||
{
|
{
|
||||||
type: "section",
|
type: "section",
|
||||||
text: {
|
text: {
|
||||||
type: "mrkdwn",
|
type: "mrkdwn",
|
||||||
text: `Order *<${this.options_.admin_orders_url}/${order._id}|#${order.display_id}>* has been processed.`,
|
text: `Order *<${this.options_.admin_orders_url}/${order.id}|#${order.display_id}>* has been processed.`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -56,15 +83,17 @@ class SlackService extends BaseService {
|
|||||||
type: "section",
|
type: "section",
|
||||||
text: {
|
text: {
|
||||||
type: "mrkdwn",
|
type: "mrkdwn",
|
||||||
text: `*Subtotal*\t${subtotal.toFixed(2)} ${
|
text: `*Subtotal*\t${(subtotal / 100).toFixed(
|
||||||
order.currency_code
|
2
|
||||||
}\n*Shipping*\t${shippingTotal.toFixed(2)} ${
|
)} ${currencyCode}\n*Shipping*\t${(shipping_total / 100).toFixed(
|
||||||
order.currency_code
|
2
|
||||||
}\n*Discount Total*\t${discountTotal.toFixed(2)} ${
|
)} ${currencyCode}\n*Discount Total*\t${(
|
||||||
order.currency_code
|
discount_total / 100
|
||||||
}\n*Tax*\t${taxTotal.toFixed(2)} ${
|
).toFixed(2)} ${currencyCode}\n*Tax*\t${(tax_total / 100).toFixed(
|
||||||
order.currency_code
|
2
|
||||||
}\n*Total*\t${total.toFixed(2)} ${order.currency_code}`,
|
)} ${currencyCode}\n*Total*\t${(total / 100).toFixed(
|
||||||
|
2
|
||||||
|
)} ${currencyCode}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@@ -74,7 +103,9 @@ class SlackService extends BaseService {
|
|||||||
type: "section",
|
type: "section",
|
||||||
text: {
|
text: {
|
||||||
type: "mrkdwn",
|
type: "mrkdwn",
|
||||||
text: `*Promo Code*\t${d.code} ${d.discount_rule.value}${d.discount_rule.type === "percentage" ? "%" : ` ${order.currency_code}`}`,
|
text: `*Promo Code*\t${d.code} ${d.rule.value}${
|
||||||
|
d.rule.type === "percentage" ? "%" : ` ${currencyCode}`
|
||||||
|
}`,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -88,12 +119,13 @@ class SlackService extends BaseService {
|
|||||||
type: "section",
|
type: "section",
|
||||||
text: {
|
text: {
|
||||||
type: "mrkdwn",
|
type: "mrkdwn",
|
||||||
text: `*${lineItem.title}*\n${lineItem.quantity} x ${
|
text: `*${lineItem.title}*\n${lineItem.quantity} x ${(
|
||||||
!Array.isArray(lineItem.content) &&
|
(lineItem.unit_price / 100) *
|
||||||
(lineItem.content.unit_price * (1 + order.tax_rate)).toFixed(2)
|
(1 + taxRate)
|
||||||
} ${order.currency_code}`,
|
).toFixed(2)} ${currencyCode}`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lineItem.thumbnail) {
|
if (lineItem.thumbnail) {
|
||||||
let url = lineItem.thumbnail
|
let url = lineItem.thumbnail
|
||||||
if (
|
if (
|
||||||
@@ -106,9 +138,8 @@ class SlackService extends BaseService {
|
|||||||
line.accessory = {
|
line.accessory = {
|
||||||
type: "image",
|
type: "image",
|
||||||
alt_text: "Item",
|
alt_text: "Item",
|
||||||
image_url: url
|
image_url: url,
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
blocks.push(line)
|
blocks.push(line)
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ class OrderSubscriber {
|
|||||||
|
|
||||||
this.eventBus_ = eventBusService
|
this.eventBus_ = eventBusService
|
||||||
|
|
||||||
this.eventBus_.subscribe("order.placed", async (order) => {
|
this.eventBus_.subscribe("order.placed", async ({ id }) => {
|
||||||
await this.slackService_.orderNotification(order._id)
|
await this.slackService_.orderNotification(id)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
export default [
|
|
||||||
"BE",
|
|
||||||
"BG",
|
|
||||||
"CZ",
|
|
||||||
"DK",
|
|
||||||
"DE",
|
|
||||||
"EE",
|
|
||||||
"IE",
|
|
||||||
"EL",
|
|
||||||
"ES",
|
|
||||||
"FR",
|
|
||||||
"HR",
|
|
||||||
"IT",
|
|
||||||
"CY",
|
|
||||||
"LV",
|
|
||||||
"LT",
|
|
||||||
"LU",
|
|
||||||
"HU",
|
|
||||||
"MT",
|
|
||||||
"NL",
|
|
||||||
"AT",
|
|
||||||
"PL",
|
|
||||||
"PT",
|
|
||||||
"RO",
|
|
||||||
"SI",
|
|
||||||
"SK",
|
|
||||||
"FI",
|
|
||||||
"SE",
|
|
||||||
"UK",
|
|
||||||
]
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
|
|
||||||
Object.defineProperty(exports, "__esModule", {
|
|
||||||
value: true
|
|
||||||
});
|
|
||||||
exports["default"] = void 0;
|
|
||||||
var _default = ["BE", "BG", "CZ", "DK", "DE", "EE", "IE", "EL", "ES", "FR", "HR", "IT", "CY", "LV", "LT", "LU", "HU", "MT", "NL", "AT", "PL", "PT", "RO", "SI", "SK", "FI", "SE", "UK"];
|
|
||||||
exports["default"] = _default;
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-plugin-twilio-sms",
|
"name": "medusa-plugin-twilio-sms",
|
||||||
"version": "1.0.12",
|
"version": "1.0.11-alpha.176+0646bd3",
|
||||||
"description": "Twilio SMS service",
|
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -36,9 +35,9 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/plugin-transform-classes": "^7.9.5",
|
"@babel/plugin-transform-classes": "^7.9.5",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"medusa-core-utils": "^1.0.11",
|
"medusa-core-utils": "^1.0.12-alpha.787+0646bd3",
|
||||||
"medusa-test-utils": "^1.0.13",
|
"medusa-test-utils": "^1.0.12-alpha.176+0646bd3",
|
||||||
"twilio": "^3.49.1"
|
"twilio": "^3.49.1"
|
||||||
},
|
},
|
||||||
"gitHead": "e1c465d244b20e68dd88bcb12db0a061f25581ec"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-plugin-wishlist",
|
"name": "medusa-plugin-wishlist",
|
||||||
"version": "1.0.13",
|
"version": "1.0.12-alpha.176+0646bd3",
|
||||||
"description": "Provides /customers/:id/wishlist to add items to a customr's wishlist",
|
"description": "Provides /customers/:id/wishlist to add items to a customr's wishlist",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -37,8 +37,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"medusa-core-utils": "^1.0.11",
|
"medusa-core-utils": "^1.0.12-alpha.787+0646bd3",
|
||||||
"medusa-test-utils": "^1.0.13"
|
"medusa-test-utils": "^1.0.12-alpha.176+0646bd3"
|
||||||
},
|
},
|
||||||
"gitHead": "3cc7cbe5124cbcbb75f6e1435db4dcfaa2a60408"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-test-utils",
|
"name": "medusa-test-utils",
|
||||||
"version": "1.0.13",
|
"version": "1.0.12-alpha.176+0646bd3",
|
||||||
"description": "Test utils for Medusa",
|
"description": "Test utils for Medusa",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -29,8 +29,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/plugin-transform-classes": "^7.9.5",
|
"@babel/plugin-transform-classes": "^7.9.5",
|
||||||
"medusa-core-utils": "^1.0.11",
|
"medusa-core-utils": "^1.0.12-alpha.787+0646bd3",
|
||||||
"mongoose": "^5.8.0"
|
"randomatic": "^3.1.1"
|
||||||
},
|
},
|
||||||
"gitHead": "3cc7cbe5124cbcbb75f6e1435db4dcfaa2a60408"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -1,23 +1,19 @@
|
|||||||
import mongoose from "mongoose"
|
import randomize from "randomatic";
|
||||||
|
|
||||||
String.prototype.equals = function(that) {
|
|
||||||
return this === that
|
|
||||||
}
|
|
||||||
|
|
||||||
class IdMap {
|
class IdMap {
|
||||||
ids = {}
|
ids = {};
|
||||||
|
|
||||||
getId(key, backend=false) {
|
getId(key, prefix = "", length = 10) {
|
||||||
if (this.ids[key]) {
|
if (this.ids[key]) {
|
||||||
return this.ids[key]
|
return this.ids[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
const mongooseId = `${mongoose.Types.ObjectId()}`
|
const id = `${prefix && prefix + "_"}${randomize("Aa0", length)}`;
|
||||||
this.ids[key] = mongooseId
|
this.ids[key] = id;
|
||||||
|
|
||||||
return mongooseId
|
return id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const instance = new IdMap()
|
const instance = new IdMap();
|
||||||
export default instance
|
export default instance;
|
||||||
@@ -1 +1,3 @@
|
|||||||
export { default as IdMap } from "./id-map"
|
export { default as IdMap } from "./id-map";
|
||||||
|
export { default as MockRepository } from "./mock-repository";
|
||||||
|
export { default as MockManager } from "./mock-manager";
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
export default {
|
||||||
|
getCustomRepository: function (repo) {
|
||||||
|
return repo;
|
||||||
|
},
|
||||||
|
|
||||||
|
transaction: function (isolationOrCb, cb) {
|
||||||
|
if (typeof isolationOrCb === "string") {
|
||||||
|
return cb(this);
|
||||||
|
} else {
|
||||||
|
return isolationOrCb(this);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
export default ({
|
||||||
|
create,
|
||||||
|
update,
|
||||||
|
remove,
|
||||||
|
softRemove,
|
||||||
|
find,
|
||||||
|
findOne,
|
||||||
|
findOneOrFail,
|
||||||
|
save,
|
||||||
|
} = {}) => {
|
||||||
|
return {
|
||||||
|
create: jest.fn().mockImplementation((...args) => {
|
||||||
|
if (create) {
|
||||||
|
return create(...args);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}),
|
||||||
|
softRemove: jest.fn().mockImplementation((...args) => {
|
||||||
|
if (softRemove) {
|
||||||
|
return softRemove(...args);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}),
|
||||||
|
remove: jest.fn().mockImplementation((...args) => {
|
||||||
|
if (remove) {
|
||||||
|
return remove(...args);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}),
|
||||||
|
update: jest.fn().mockImplementation((...args) => {
|
||||||
|
if (update) {
|
||||||
|
return update(...args);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
findOneOrFail: jest.fn().mockImplementation((...args) => {
|
||||||
|
if (findOneOrFail) {
|
||||||
|
return findOneOrFail(...args);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
findOne: jest.fn().mockImplementation((...args) => {
|
||||||
|
if (findOne) {
|
||||||
|
return findOne(...args);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
findOneOrFail: jest.fn().mockImplementation((...args) => {
|
||||||
|
if (findOneOrFail) {
|
||||||
|
return findOneOrFail(...args);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
find: jest.fn().mockImplementation((...args) => {
|
||||||
|
if (find) {
|
||||||
|
return find(...args);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
softRemove: jest.fn().mockImplementation((...args) => {
|
||||||
|
if (softRemove) {
|
||||||
|
return softRemove(...args);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
save: jest.fn().mockImplementation((...args) => {
|
||||||
|
if (save) {
|
||||||
|
return save(...args);
|
||||||
|
}
|
||||||
|
return Promise.resolve(...args);
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
};
|
||||||
Loaded 100 of 499 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user