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
+25909
-16128
No files matched your search
@@ -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(
|
||||
"/capture",
|
||||
"/notification",
|
||||
bodyParser.json(),
|
||||
middlewares.wrap(require("./capture-hook").default)
|
||||
middlewares.wrap(require("./adyen-notification").default)
|
||||
)
|
||||
|
||||
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)
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
@@ -13,39 +13,44 @@ export default async (req, res) => {
|
||||
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 cart = await cartService.retrieve(value.cart_id, {
|
||||
select: ["total"],
|
||||
relations: ["region", "region.payment_providers", "payment_sessions"],
|
||||
})
|
||||
|
||||
const allowedMethods = cart.payment_sessions.map(
|
||||
(ps) => ps.provider_id.split("Adyen")[0]
|
||||
)
|
||||
const allowedMethods = cart.payment_sessions.map((ps) => {
|
||||
if (ps.provider_id.includes("adyen")) {
|
||||
return ps.provider_id.split("-adyen")[0]
|
||||
}
|
||||
})
|
||||
|
||||
if (allowedMethods.length === 0) {
|
||||
res.status(200).json({ paymentMethods: {} })
|
||||
return
|
||||
}
|
||||
|
||||
const { data } = await adyenService.retrievePaymentMethods(
|
||||
cart,
|
||||
const pmMethods = await adyenService.retrievePaymentMethods(
|
||||
allowedMethods,
|
||||
total,
|
||||
region.currency_code
|
||||
cart.total,
|
||||
cart.currency_code,
|
||||
cart.customer_id || ""
|
||||
)
|
||||
|
||||
// Adyen does not behave 100% correctly in regards to allowed methods
|
||||
// Therefore, we sanity filter before sending them to the storefront
|
||||
const { paymentMethods, groups } = data
|
||||
const { paymentMethods, groups, storedPaymentMethods } = pmMethods
|
||||
const methods = paymentMethods.filter((pm) =>
|
||||
allowedMethods.includes(pm.type)
|
||||
)
|
||||
|
||||
res
|
||||
.status(200)
|
||||
.json({ paymentMethods: { paymentMethods: methods, groups } })
|
||||
const response = {
|
||||
paymentMethods: methods,
|
||||
groups,
|
||||
storedPaymentMethods,
|
||||
}
|
||||
|
||||
res.status(200).json({ payment_methods: response })
|
||||
} catch (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 { hmacValidator } from "@adyen/api-library"
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
import { Client, Config, CheckoutAPI } from "@adyen/api-library"
|
||||
|
||||
class AdyenService extends BaseService {
|
||||
constructor({ regionService, cartService, totalsService }, options) {
|
||||
constructor({ cartService }, options) {
|
||||
super()
|
||||
|
||||
this.regionService_ = regionService
|
||||
|
||||
/** @private @constant {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.adyenCheckoutApi = axios.create({
|
||||
baseURL: "https://checkout-test.adyen.com/v53",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"x-API-key": this.options_.api_key,
|
||||
},
|
||||
/** @private @constant {AxiosClient} */
|
||||
this.adyenClient_ = this.initAdyenClient()
|
||||
|
||||
/** @private @constant {AdyenClient} */
|
||||
this.adyenPaymentApi = this.initPaymentClient()
|
||||
}
|
||||
|
||||
withTransaction(transactionManager) {
|
||||
if (!transactionManager) {
|
||||
return this
|
||||
}
|
||||
|
||||
const cloned = new AdyenService({
|
||||
cartService: this.cartService_,
|
||||
totalsService: this.totalsService_,
|
||||
})
|
||||
|
||||
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,
|
||||
},
|
||||
})
|
||||
this.transactionManager_ = transactionManager
|
||||
|
||||
return cloned
|
||||
}
|
||||
|
||||
getOptions() {
|
||||
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 validated = validator.validateHMAC(
|
||||
event,
|
||||
notification,
|
||||
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
|
||||
* Retrieve stored payment methods from Adyen.
|
||||
* @param {Customer} customer - customer to retrieve methods for
|
||||
* @returns {Promise} result containing the stored payment methods from Adyen
|
||||
*/
|
||||
async retrievePaymentMethods(cart, allowedMethods, total, currency) {
|
||||
async retrieveSavedMethods(customer) {
|
||||
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
|
||||
channel: "Web",
|
||||
shopperReference: customer.id,
|
||||
}
|
||||
|
||||
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) {
|
||||
throw error
|
||||
}
|
||||
@@ -75,89 +144,218 @@ class AdyenService extends BaseService {
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
async getStatus(_) {
|
||||
let status = "initial"
|
||||
getStatus(paymentData) {
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Adyen payment object.
|
||||
* @param {any} _ - placeholder object
|
||||
* @returns {Object} empty payment data
|
||||
* @param {Cart} cart - cart to initiate payment for
|
||||
* @returns {object} empty payment data
|
||||
*/
|
||||
async createPayment(_) {
|
||||
return {}
|
||||
}
|
||||
|
||||
async retrievePayment(data) {
|
||||
return data
|
||||
}
|
||||
|
||||
async updatePayment(paymentData, details) {
|
||||
const request = {
|
||||
paymentData,
|
||||
details,
|
||||
}
|
||||
return this.adyenCheckoutApi.post("/payments/details", request)
|
||||
async createPayment(cart) {
|
||||
return { cart_id: cart.id }
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and authorizes an Ayden payment
|
||||
* @returns {Object} payment data result
|
||||
* Retrieves Adyen payment. This is not supported by adyen, so we simply
|
||||
* return the current payment method data
|
||||
* @param {object} data - payment session
|
||||
* @returns {object} payment method data
|
||||
*/
|
||||
async authorizePayment(cart, paymentMethod) {
|
||||
const region = await this.regionService_.retrieve(cart.region_id)
|
||||
const total = await this.totalsService_.getTotal(cart)
|
||||
async getPaymentData(paymentSession) {
|
||||
return { ...paymentSession.data }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = {
|
||||
amount: {
|
||||
currency: region.currency_code,
|
||||
value: total * 100,
|
||||
},
|
||||
amount,
|
||||
shopperIP: context.ip_address || "",
|
||||
shopperReference: cart.customer_id,
|
||||
paymentMethod,
|
||||
reference: cart._id,
|
||||
paymentMethod: sessionData.paymentData.paymentMethod,
|
||||
reference: cart.id,
|
||||
merchantAccount: this.options_.merchant_account,
|
||||
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: {
|
||||
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.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) {
|
||||
const request = {
|
||||
paymentData,
|
||||
details: {
|
||||
payload,
|
||||
},
|
||||
async updatePaymentData(sessionData, update) {
|
||||
if (_.isEmpty(update.details)) {
|
||||
return { ...sessionData, ...update }
|
||||
}
|
||||
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
|
||||
* @param {Object} data - payment data to capture
|
||||
* @returns {Object} payment data result of capture
|
||||
* Updates an Adyen payment.
|
||||
* @param {object} paymentData - payment data to update
|
||||
* @param {details} details - details to update
|
||||
* @returns {Promise} result of the update operation
|
||||
*/
|
||||
async capturePayment(data) {
|
||||
const { pspReference, amount } = data
|
||||
async updatePayment(paymentData, details) {
|
||||
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 {
|
||||
const captured = await this.adyenPaymentApi.post("/capture", {
|
||||
originalReference: pspReference,
|
||||
modificationAmount: amount,
|
||||
modificationAmount: {
|
||||
value: amount,
|
||||
currency: currency_code.toUpperCase(),
|
||||
},
|
||||
merchantAccount: this.options_.merchant_account,
|
||||
reference: merchantReference,
|
||||
})
|
||||
|
||||
if (
|
||||
@@ -170,40 +368,55 @@ class AdyenService extends BaseService {
|
||||
)
|
||||
}
|
||||
|
||||
return captured
|
||||
return { originalReference: pspReference, ...captured.data }
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refunds an Ayden payment
|
||||
* @param {Object} paymentData - payment data to refund
|
||||
* Refunds an Adyen payment
|
||||
* @param {object} payment - payment 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) {
|
||||
const { pspReference, amount } = data
|
||||
async refundPayment(payment, amountToRefund) {
|
||||
const { originalReference, merchantReference } = payment.data
|
||||
const { currency_code } = payment
|
||||
|
||||
const refundAmount = {
|
||||
currency: currency_code.toUpperCase(),
|
||||
value: amountToRefund,
|
||||
}
|
||||
|
||||
try {
|
||||
return this.adyenPaymentApi.post("/refund", {
|
||||
originalReference: pspReference,
|
||||
const refunded = await this.adyenPaymentApi.post("/refund", {
|
||||
originalReference,
|
||||
merchantAccount: this.options_.merchant_account,
|
||||
modificationAmount: amount,
|
||||
modificationAmount: refundAmount,
|
||||
reference: merchantReference,
|
||||
})
|
||||
|
||||
return { originalReference, ...refunded.data }
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels an Ayden payment
|
||||
* @param {Object} paymentData - payment data to cancel
|
||||
* @returns {Object} payment data result of cancel
|
||||
* Adyen does not have a way of deleting payments, hence the empty impl.
|
||||
*/
|
||||
async cancelPayment(paymentData) {
|
||||
const { pspReference } = paymentData
|
||||
async deletePayment(_) {
|
||||
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 {
|
||||
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
|
||||
Reference in new issue
Block a user