Adds dynamic coupon codes and segment plugin
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"plugins": [
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
"@babel/plugin-transform-instanceof",
|
||||
"@babel/plugin-transform-classes"
|
||||
],
|
||||
"presets": ["@babel/preset-env"],
|
||||
"env": {
|
||||
"test": {
|
||||
"plugins": ["@babel/plugin-transform-runtime"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"plugins": ["prettier"],
|
||||
"extends": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"semi": "error",
|
||||
"no-unused-expressions": "true"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/lib
|
||||
node_modules
|
||||
.DS_store
|
||||
.env*
|
||||
/*.js
|
||||
!index.js
|
||||
!jest.config.js
|
||||
|
||||
/dist
|
||||
|
||||
/api
|
||||
/services
|
||||
/models
|
||||
/subscribers
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
/lib
|
||||
node_modules
|
||||
.DS_store
|
||||
.env*
|
||||
/*.js
|
||||
!index.js
|
||||
yarn.lock
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
// noop
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
testEnvironment: "node",
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "medusa-plugin-segment",
|
||||
"version": "0.3.0",
|
||||
"description": "Segment Analytics",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/medusajs/medusa",
|
||||
"directory": "packages/medusa-plugin-segment"
|
||||
},
|
||||
"author": "Sebastian Rindom",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.7.5",
|
||||
"@babel/core": "^7.7.5",
|
||||
"@babel/node": "^7.7.4",
|
||||
"@babel/plugin-proposal-class-properties": "^7.7.4",
|
||||
"@babel/plugin-transform-classes": "^7.9.5",
|
||||
"@babel/plugin-transform-instanceof": "^7.8.3",
|
||||
"@babel/plugin-transform-runtime": "^7.7.6",
|
||||
"@babel/preset-env": "^7.7.5",
|
||||
"@babel/register": "^7.7.4",
|
||||
"@babel/runtime": "^7.9.6",
|
||||
"cross-env": "^5.2.1",
|
||||
"eslint": "^6.8.0",
|
||||
"jest": "^25.5.2"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "babel src -d .",
|
||||
"prepare": "cross-env NODE_ENV=production npm run build",
|
||||
"watch": "babel -w src --out-dir . --ignore **/__tests__",
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"analytics-node": "^3.4.0-beta.1",
|
||||
"axios": "^0.19.2",
|
||||
"body-parser": "^1.19.0",
|
||||
"express": "^4.17.1",
|
||||
"medusa-core-utils": "^0.3.0",
|
||||
"medusa-interfaces": "^0.3.0",
|
||||
"medusa-test-utils": "^0.3.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
import Analytics from "analytics-node"
|
||||
import axios from "axios"
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
|
||||
class SegmentService extends BaseService {
|
||||
/**
|
||||
* @param {Object} options - options defined in `medusa-config.js`
|
||||
* e.g.
|
||||
* {
|
||||
* write_key: Segment write key given in Segment dashboard
|
||||
* }
|
||||
*/
|
||||
constructor({totalsService}, options) {
|
||||
super()
|
||||
|
||||
this.options_ = options
|
||||
this.totalsService_ = totalsService
|
||||
|
||||
this.analytics_ = new Analytics(options.write_key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around segment's identify call
|
||||
*/
|
||||
identify(data) {
|
||||
return this.analytics_.identify(data)
|
||||
}
|
||||
|
||||
track(data) {
|
||||
return this.analytics_.track(data)
|
||||
}
|
||||
|
||||
async getReportingValue(fromCurrency, value) {
|
||||
const date = "latest"
|
||||
const toCurrency =
|
||||
(this.options_.reporting_currency &&
|
||||
this.options_.reporting_currency.toUpperCase()) ||
|
||||
"EUR"
|
||||
|
||||
if (fromCurrency === toCurrency) return value.toFixed(2)
|
||||
|
||||
const exchangeRate = await axios
|
||||
.get(`https://api.exchangeratesapi.io/${date}?symbols=${fromCurrency}&base=${toCurrency}`)
|
||||
.then(({ data }) => {
|
||||
return data.rates[fromCurrency]
|
||||
})
|
||||
|
||||
return (value / exchangeRate).toFixed(2)
|
||||
}
|
||||
|
||||
|
||||
async buildOrder(order) {
|
||||
console.log("build", order)
|
||||
const subtotal = await this.totalsService_.getSubtotal(order)
|
||||
const total = await this.totalsService_.getTotal(order)
|
||||
const tax = await this.totalsService_.getTaxTotal(order)
|
||||
const discount = await this.totalsService_.getDiscountTotal(order)
|
||||
const shipping = await this.totalsService_.getShippingTotal(order)
|
||||
const revenue = total - tax
|
||||
|
||||
let coupon
|
||||
if (order.discounts && order.discounts.length) {
|
||||
coupon = order.discounts[0].code
|
||||
}
|
||||
|
||||
const orderData = {
|
||||
checkout_id: order.cart_id,
|
||||
order_id: order._id,
|
||||
email: order.email,
|
||||
reporting_total: await this.getReportingValue(order.currency_code, total),
|
||||
reporting_subtotal: await this.getReportingValue(order.currency_code, subtotal),
|
||||
reporting_revenue: await this.getReportingValue(order.currency_code, revenue),
|
||||
reporting_shipping: await this.getReportingValue(order.currency_code, shipping),
|
||||
reporting_tax: await this.getReportingValue(order.currency_code, tax),
|
||||
reporting_discount: await this.getReportingValue(order.currency_code, discount),
|
||||
total,
|
||||
subtotal,
|
||||
revenue,
|
||||
shipping,
|
||||
tax,
|
||||
discount,
|
||||
coupon,
|
||||
currency: order.currency_code,
|
||||
products: await Promise.all(
|
||||
order.items.map(async item => {
|
||||
let name = item.title
|
||||
let variant = item.description
|
||||
|
||||
const unit_price = item.content.unit_price
|
||||
const line_total = unit_price * item.content.quantity * item.quantity
|
||||
const revenue = await this.getReportingValue(order.currency_code, line_total)
|
||||
return {
|
||||
name,
|
||||
variant,
|
||||
price: unit_price,
|
||||
reporting_revenue: revenue,
|
||||
product_id: `${item.content.product._id}`,
|
||||
sku: item.content.variant.sku,
|
||||
quantity: item.quantity,
|
||||
}
|
||||
})
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
return orderData
|
||||
}
|
||||
}
|
||||
|
||||
export default SegmentService
|
||||
@@ -0,0 +1,18 @@
|
||||
class OrderSubscriber {
|
||||
constructor({ segmentService, eventBusService }) {
|
||||
eventBusService.subscribe("order.placed", async (order) => {
|
||||
const date = new Date(parseInt(order.created))
|
||||
const orderData = await segmentService.buildOrder(order)
|
||||
const orderEvent = {
|
||||
event: "Order Completed",
|
||||
userId: order.customer_id,
|
||||
properties: orderData,
|
||||
timestamp: date,
|
||||
}
|
||||
|
||||
segmentService.track(orderEvent)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default OrderSubscriber
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user