Adds Adyen payment provider

This commit is contained in:
olivermrbl
2020-08-06 12:12:31 +02:00
parent 4c9b876407
commit 20c8dc23a1
30 changed files with 1047 additions and 16 deletions

View File

@@ -3,5 +3,8 @@
"private": true,
"devDependencies": {
"lerna": "^3.19.0"
},
"dependencies": {
"axios": "^0.19.2"
}
}

View File

@@ -12,7 +12,7 @@ Joi.address = () => {
country_code: Joi.string().required(),
province: Joi.string().allow(""),
postal_code: Joi.string().required(),
phone: Joi.string(),
phone: Joi.string().optional(),
metadata: Joi.object(),
})
}

View File

@@ -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"]
}
}
}

View File

@@ -0,0 +1,9 @@
{
"plugins": ["prettier"],
"extends": ["prettier"],
"rules": {
"prettier/prettier": "error",
"semi": "error",
"no-unused-expressions": "true"
}
}

View File

@@ -0,0 +1,16 @@
/lib
node_modules
.DS_store
.env*
/*.js
!index.js
yarn.lock
/dist
/api
/services
/models
/subscribers
/__mocks__

View File

@@ -0,0 +1,9 @@
/lib
node_modules
.DS_store
.env*
/*.js
!index.js
yarn.lock

View File

@@ -0,0 +1,7 @@
{
"endOfLine": "lf",
"semi": false,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "es5"
}

View File

@@ -0,0 +1 @@
// noop

View File

@@ -0,0 +1,47 @@
{
"name": "medusa-payment-adyen",
"version": "0.3.0",
"description": "Adyen Payment provider for Medusa Commerce",
"main": "index.js",
"repository": {
"type": "git",
"url": "https://github.com/medusajs/medusa",
"directory": "packages/medusa-payment-adyen"
},
"author": "Oliver Juhl",
"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-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",
"client-sessions": "^0.8.0",
"cross-env": "^5.2.1",
"eslint": "^6.8.0",
"jest": "^25.5.2",
"medusa-test-utils": "^0.3.0"
},
"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": {
"@adyen/api-library": "^5.0.1",
"@babel/plugin-transform-classes": "^7.9.5",
"axios": "^0.19.2",
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"express": "^4.17.1",
"medusa-core-utils": "^0.1.27",
"medusa-interfaces": "^0.3.0",
"medusa-test-utils": "^0.3.0"
},
"gitHead": "35e0930650d5f4aedf2610749cd131ae8b7e17cc"
}

View File

@@ -0,0 +1,10 @@
import { Router } from "express"
import store from "./routes/store"
import hooks from "./routes/hooks"
export default (rootDirectory) => {
const app = Router()
store(app, rootDirectory)
hooks(app, rootDirectory)
return app
}

View File

@@ -0,0 +1 @@
export default (fn) => (...args) => fn(...args).catch(args[2])

View File

@@ -0,0 +1,5 @@
import { default as wrap } from "./await-middleware"
export default {
wrap,
}

View File

@@ -0,0 +1,22 @@
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
}
switch (event.success) {
case "false":
res.status(400).send(event.reason)
return
default:
res.status(200).send("[accepted]")
return
}
}

View File

@@ -0,0 +1,30 @@
import { Router } from "express"
import cors from "cors"
import bodyParser from "body-parser"
import middlewares from "../../middlewares"
import { getConfigFile } from "medusa-core-utils"
const route = Router()
export default (app, rootDirectory) => {
const { configModule } = getConfigFile(rootDirectory, `medusa-config`)
const config = (configModule && configModule.projectConfig) || {}
const storeCors = config.store_cors || ""
route.use(
cors({
origin: storeCors.split(","),
credentials: true,
})
)
app.use("/adyen-hooks", route)
route.post(
"/capture",
bodyParser.json(),
middlewares.wrap(require("./capture-hook").default)
)
return app
}

View File

@@ -0,0 +1,53 @@
import { Validator, MedusaError } from "medusa-core-utils"
export default async (req, res) => {
const schema = Validator.object().keys({
cart_id: Validator.string().required(),
payment_method: Validator.object().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 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_method
)
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
)
res.status(200).json({ data })
} catch (err) {
throw err
}
}

View File

@@ -0,0 +1,32 @@
import { Validator, MedusaError } from "medusa-core-utils"
export default async (req, res) => {
const schema = Validator.object().keys({
payment_provider: Validator.string().required(),
payload: Validator.object().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 adyenService = req.scope.resolve("adyenService")
const paymentProviderService = req.scope.resolve(
`${value.payment_provider}AdyenProviderService`
)
const adyenResultCode = await adyenService.checkPaymentResult(
value.payment_data,
value.payload
)
const status = paymentProviderService.getStatus(adyenResultCode)
res.status(200).json({ status })
} catch (err) {
throw err
}
}

View File

@@ -0,0 +1,42 @@
import { Router } from "express"
import cors from "cors"
import bodyParser from "body-parser"
import middlewares from "../../middlewares"
import { getConfigFile } from "medusa-core-utils"
const route = Router()
export default (app, rootDirectory) => {
const { configModule } = getConfigFile(rootDirectory, `medusa-config`)
const config = (configModule && configModule.projectConfig) || {}
const storeCors = config.store_cors || ""
route.use(
cors({
origin: storeCors.split(","),
credentials: true,
})
)
app.use("/adyen", route)
route.post(
"/payment-methods",
bodyParser.json(),
middlewares.wrap(require("./retrieve-payment-methods").default)
)
route.post(
"/authorize",
bodyParser.json(),
middlewares.wrap(require("./authorize-payment").default)
)
route.post(
"/payment-status",
bodyParser.json(),
middlewares.wrap(require("./check-payment-status").default)
)
return app
}

View File

@@ -0,0 +1,38 @@
import { Validator, MedusaError } from "medusa-core-utils"
export default async (req, res) => {
const schema = Validator.object().keys({
cart_id: Validator.string().required(),
})
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
try {
const adyenService = req.scope.resolve("adyenService")
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 region = await regionService.retrieve(cart.region_id)
const total = await totalsService.getTotal(cart)
const allowedMethods = cart.payment_sessions.map(
(ps) => ps.provider_id.split("Adyen")[0]
)
const { data } = await adyenService.retrievePaymentMethods(
cart,
allowedMethods,
total,
region.currency_code
)
res.status(200).json({ paymentMethods: data })
} catch (err) {
throw err
}
}

View File

@@ -0,0 +1,199 @@
import axios from "axios"
import _ from "lodash"
import { hmacValidator } from "@adyen/api-library"
import { BaseService } from "medusa-interfaces"
class AdyenService extends BaseService {
constructor({ regionService, cartService, totalsService }, options) {
super()
this.regionService_ = regionService
this.cartService_ = cartService
this.totalsService_ = totalsService
this.options_ = options
this.adyenCheckoutApi = axios.create({
baseURL: "https://checkout-test.adyen.com/v52",
headers: {
"Content-Type": "application/json",
"x-API-key": this.options_.api_key,
},
})
this.adyenPaymentApi = axios.create({
baseURL: "https://pal-test.adyen.com/pal/servlet/Payment/v52",
headers: {
"Content-Type": "application/json",
"x-API-key": this.options_.api_key,
},
})
}
getOptions() {
return this.options_
}
validateNotification(event) {
const validator = new hmacValidator()
const validated = validator.validateHMAC(
event,
this.options_.notification_hmac
)
return validated
}
/**
* Retrieve payment methods from Ayden using country as filter.
* @param {string} countryCode - country code of cart
* @param {string} shopperLocale - locale used on website
* @returns {string} the status of the payment
*/
async retrievePaymentMethods(cart, allowedMethods, total, currency) {
let request = {
allowedPaymentMethods: allowedMethods,
amount: {
value: total * 100,
currency: currency,
},
merchantAccount: this.options_.merchant_account,
channel: this.options_.channel,
}
if (cart.customer_id) {
request.shopperReference = cart.customer_id
}
try {
return await this.adyenCheckoutApi.post("/paymentMethods", request)
} catch (error) {
throw error
}
}
/**
* Status for Adyen payment.
* @param {Object} paymentData - payment method data from cart
* @returns {string} the status of the payment
*/
async getStatus(_) {
let status = "initial"
return status
}
/**
* Creates Adyen payment object.
* @param {any} _ - placeholder object
* @returns {Object} empty payment data
*/
async createPayment(_) {
return {}
}
async retrievePayment(data) {
return data
}
/**
* Creates and authorizes an Ayden payment
* @returns {Object} payment data result
*/
async authorizePayment(cart, paymentMethod) {
const region = await this.regionService_.retrieve(cart.region_id)
const total = await this.totalsService_.getTotal(cart)
let request = {
amount: {
currency: region.currency_code,
value: total * 100,
},
shopperReference: cart.customer_id,
paymentMethod,
reference: cart._id,
merchantAccount: this.options_.merchant_account,
returnUrl: this.options_.return_url,
metadata: {
cart_id: cart._id,
},
}
if (paymentMethod.storedPaymentMethodId) {
request.shopperInteraction = "Ecommerce"
request.recurringProcessingModel = "CardOnFile"
}
return await this.adyenCheckoutApi.post("/payments", request)
}
async checkPaymentResult(paymentData, payload) {
const request = {
paymentData,
details: {
payload,
},
}
return this.adyenCheckoutApi.post("/payments/details", request)
}
/**
* Captures an Ayden payment
* @param {Object} data - payment data to capture
* @returns {Object} payment data result of capture
*/
async capturePayment(data) {
const { pspReference, amount } = data
try {
return this.adyenPaymentApi.post("/capture", {
originalReference: pspReference,
modificationAmount: amount,
merchantAccount: this.options_.merchant_account,
})
} catch (error) {
console.log(error)
throw error
}
}
/**
* Refunds an Ayden payment
* @param {Object} paymentData - payment data to refund
* @param {number} amountToRefund - amount to refund
* @returns {Object} payment data result of refund
*/
async refundPayment(data) {
const { pspReference, amount } = data
try {
return this.adyenPaymentApi.post("/capture", {
originalReference: pspReference,
merchantAccount: this.options_.merchant_account,
modificationAmount: amount,
})
} catch (error) {
throw error
}
}
/**
* Cancels an Ayden payment
* @param {Object} paymentData - payment data to cancel
* @returns {Object} payment data result of cancel
*/
async cancelPayment(paymentData) {
const { pspReference } = paymentData
try {
return this.adyenPaymentApi.post("/capture", {
originalReference: pspReference,
merchantAccount: this.options_.merchant_account,
})
} catch (error) {
throw error
}
}
}
export default AdyenService

View File

@@ -0,0 +1,74 @@
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

View File

@@ -0,0 +1,74 @@
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

View File

@@ -0,0 +1,74 @@
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

View File

@@ -0,0 +1,74 @@
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

View File

@@ -0,0 +1,74 @@
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 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 MobilePayAdyenService

View File

@@ -0,0 +1,74 @@
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

View File

@@ -51,7 +51,7 @@ export default ({ rootDirectory, container, app }) => {
registerModels(pluginDetails, container)
registerServices(pluginDetails, container)
registerMedusaApi(pluginDetails, container)
registerApi(pluginDetails, app)
registerApi(pluginDetails, app, rootDirectory)
registerCoreRouters(pluginDetails, container)
registerSubscribers(pluginDetails, container)
})
@@ -111,10 +111,10 @@ function registerCoreRouters(pluginDetails, container) {
/**
* Registers the plugin's api routes.
*/
function registerApi(pluginDetails, app) {
function registerApi(pluginDetails, app, rootDirectory = "") {
try {
const routes = require(`${pluginDetails.resolve}/api`).default
app.use("/", routes())
app.use("/", routes(rootDirectory))
return app
} catch (err) {
return app

View File

@@ -817,13 +817,13 @@ class CartService extends BaseService {
return null
}
const data = await this.paymentProviderService_.updateSession(
pSession,
cart
)
// const data = await this.paymentProviderService_.updateSession(
// pSession,
// cart
// )
return {
provider_id: pSession.provider_id,
data,
data: {},
}
})
)
@@ -981,13 +981,18 @@ class CartService extends BaseService {
newMethods.push(option)
}
const finalMethods = newMethods.map(m => {
const { _id, ...rest } = m
return rest
})
return this.cartModel_
.updateOne(
{
_id: cart._id,
},
{
$set: { shipping_methods: newMethods },
$set: { shipping_methods: finalMethods },
}
)
.then(result => {

View File

@@ -179,6 +179,28 @@ class OrderService extends BaseService {
return order
}
/**
* Gets an order by metadata key value pair.
* @param {string} key - key of metadata
* @param {string} value - value of metadata
* @return {Promise<Order>} the order document
*/
async retrieveByMetadata(key, value) {
const order = await this.orderModel_
.findOne({ metadata: { [key]: value } })
.catch(err => {
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
})
if (!order) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`Order with metadata ${key}: ${value} was not found`
)
}
return order
}
/**
* Checks the existence of an order by cart id.
* @param {string} cartId - cart id to find order
@@ -321,6 +343,7 @@ class OrderService extends BaseService {
customer_id: cart.customer_id,
cart_id: cart._id,
currency_code: region.currency_code,
metadata: cart.metadata,
}
const orderDocument = await this.orderModel_.create([o], {
@@ -501,7 +524,19 @@ class OrderService extends BaseService {
provider_id
)
await paymentProvider.capturePayment(data)
const captureData = await paymentProvider.capturePayment(data)
// If Adyen is used as payment provider, we need to check the
// validity of the capture request
if (
captureData.data.pspReference &&
captureData.data.response !== "[capture-received]"
) {
throw new MedusaError(
MedusaError.Types.INVALID_ARGUMENT,
"Could not process capture"
)
}
return this.orderModel_.updateOne(
{

View File

@@ -29,10 +29,6 @@ class OrderSubscriber {
await this.customerService_.addAddress(order.customer_id, address)
})
this.eventBus_.subscribe("order.placed", async order => {
await this.cartService_.delete(order.cart_id)
})
}
}

View File

@@ -1069,6 +1069,13 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==
axios@^0.19.2:
version "0.19.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==
dependencies:
follow-redirects "1.5.10"
balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
@@ -1572,7 +1579,7 @@ dateformat@^3.0.0:
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
debug@3.1.0:
debug@3.1.0, debug@=3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
@@ -1976,6 +1983,13 @@ flush-write-stream@^1.0.0:
inherits "^2.0.3"
readable-stream "^2.3.6"
follow-redirects@1.5.10:
version "1.5.10"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==
dependencies:
debug "=3.1.0"
for-in@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"