diff --git a/packages/medusa-payment-adyen/src/api/routes/store/index.js b/packages/medusa-payment-adyen/src/api/routes/store/index.js deleted file mode 100644 index 4dbb57ba25..0000000000 --- a/packages/medusa-payment-adyen/src/api/routes/store/index.js +++ /dev/null @@ -1,30 +0,0 @@ -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) - ) - - return app -} diff --git a/packages/medusa-payment-adyen/src/api/routes/store/retrieve-payment-methods.js b/packages/medusa-payment-adyen/src/api/routes/store/retrieve-payment-methods.js deleted file mode 100644 index 8887f2b8e0..0000000000 --- a/packages/medusa-payment-adyen/src/api/routes/store/retrieve-payment-methods.js +++ /dev/null @@ -1,57 +0,0 @@ -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 cart = await cartService.retrieve(value.cart_id, { - select: ["total"], - relations: ["region", "region.payment_providers", "payment_sessions"], - }) - - 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 pmMethods = await adyenService.retrievePaymentMethods( - allowedMethods, - 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, storedPaymentMethods } = pmMethods - const methods = paymentMethods.filter((pm) => - allowedMethods.includes(pm.type) - ) - - const response = { - paymentMethods: methods, - groups, - storedPaymentMethods, - } - - res.status(200).json({ payment_methods: response }) - } catch (err) { - throw err - } -} diff --git a/packages/medusa-payment-adyen/src/services/adyen.js b/packages/medusa-payment-adyen/src/services/adyen.js index 4310536964..ff85570548 100644 --- a/packages/medusa-payment-adyen/src/services/adyen.js +++ b/packages/medusa-payment-adyen/src/services/adyen.js @@ -28,7 +28,7 @@ class AdyenService extends BaseService { /** @private @constant {AxiosClient} */ this.adyenClient_ = this.initAdyenClient() - /** @private @constant {AdyenClient} */ + /** @private @constant {AxiosClient} */ this.adyenPaymentApi = this.initPaymentClient() } @@ -51,16 +51,6 @@ class AdyenService extends BaseService { return this.options_ } - 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 @@ -70,14 +60,22 @@ class AdyenService extends BaseService { config, }) - client.setEnvironment( - this.options_.environment, - this.options_.live_endpoint_prefix - ) + client.setEnvironment(this.options_.environment) return client } + initPaymentClient() { + return axios.create({ + baseURL: + this.options_.payment_endpoint || "https://checkout-test.adyen.com/v67", + headers: { + "Content-Type": "application/json", + "x-API-key": this.options_.api_key, + }, + }) + } + /** * Validates an Adyen webhook notification * @param {object} notification - notification to validate @@ -86,10 +84,14 @@ class AdyenService extends BaseService { validateNotification(notification) { const validator = new hmacValidator() + console.log(notification) + console.log(this.options_.notification_hmac) + const validated = validator.validateHMAC( notification, this.options_.notification_hmac ) + return validated } @@ -228,6 +230,10 @@ class AdyenService extends BaseService { const status = this.getStatus(sessionData) + if (sessionData.resultCode === "RedirectShopper") { + return { data: sessionData, status: "requires_more" } + } + // 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") { @@ -253,38 +259,28 @@ class AdyenService extends BaseService { value: cart.total, } + let paymentData = sessionData.paymentData + if (!paymentData) { + paymentData = { + paymentMethod: { + type: sessionData.type, + }, + } + } + let request = { amount, + merchantAccount: this.options_.merchant_account, shopperIP: context.ip_address || "", shopperReference: cart.customer_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, - }, + paymentMethod: paymentData.paymentMethod, + reference: cart.id, metadata: { cart_id: cart.id, }, } - // If customer chose to save the payment method - if (sessionData.storePaymentMethod) { - request.storePaymentMethod = "true" - request.shopperInteraction = "Ecommerce" - request.recurringProcessingModel = "CardOnFile" - } - const checkout = new CheckoutAPI(this.adyenClient_) try { @@ -344,31 +340,34 @@ class AdyenService extends BaseService { * @returns {string} status = processing_captures */ async capturePayment(payment) { + if (payment.captured_at !== null) { + return + } + const { pspReference, merchantReference } = payment.data const { amount, currency_code } = payment try { - const captured = await this.adyenPaymentApi.post("/capture", { - originalReference: pspReference, - modificationAmount: { - value: amount, - currency: currency_code.toUpperCase(), - }, - merchantAccount: this.options_.merchant_account, - reference: merchantReference, - }) + const captured = await this.adyenPaymentApi.post( + `/payments/${pspReference}/captures`, + { + merchantAccount: this.options_.merchant_account, + amount: { + value: amount, + currency: currency_code.toUpperCase(), + }, + reference: merchantReference, + } + ) - if ( - captured.data.pspReference && - captured.data.response !== "[capture-received]" - ) { + if (captured.data.pspReference && captured.data.status !== "received") { throw new MedusaError( MedusaError.Types.INVALID_ARGUMENT, "Could not process capture" ) } - return { originalReference: pspReference, ...captured.data } + return { pspReference } } catch (error) { throw error } @@ -381,7 +380,7 @@ class AdyenService extends BaseService { * @returns {object} payment data result of refund */ async refundPayment(payment, amountToRefund) { - const { originalReference, merchantReference } = payment.data + const { pspReference } = payment.data const { currency_code } = payment const refundAmount = { @@ -390,14 +389,12 @@ class AdyenService extends BaseService { } try { - const refunded = await this.adyenPaymentApi.post("/refund", { - originalReference, + await this.adyenPaymentApi.post(`/payments/${pspReference}/refunds`, { merchantAccount: this.options_.merchant_account, - modificationAmount: refundAmount, - reference: merchantReference, + amount: refundAmount, }) - return { originalReference, ...refunded.data } + return { pspReference } } catch (error) { throw error } diff --git a/packages/medusa-payment-adyen/src/services/applepay-adyen.js b/packages/medusa-payment-adyen/src/services/applepay-adyen.js deleted file mode 100644 index d93ea5992e..0000000000 --- a/packages/medusa-payment-adyen/src/services/applepay-adyen.js +++ /dev/null @@ -1,104 +0,0 @@ -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 diff --git a/packages/medusa-payment-adyen/src/services/card-adyen.js b/packages/medusa-payment-adyen/src/services/card-adyen.js deleted file mode 100644 index bc6028dda9..0000000000 --- a/packages/medusa-payment-adyen/src/services/card-adyen.js +++ /dev/null @@ -1,62 +0,0 @@ -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 diff --git a/packages/medusa-payment-adyen/src/services/googlepay-adyen.js b/packages/medusa-payment-adyen/src/services/googlepay-adyen.js deleted file mode 100644 index 2a24847d88..0000000000 --- a/packages/medusa-payment-adyen/src/services/googlepay-adyen.js +++ /dev/null @@ -1,58 +0,0 @@ -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 diff --git a/packages/medusa-payment-adyen/src/services/ideal-adyen.js b/packages/medusa-payment-adyen/src/services/ideal-adyen.js deleted file mode 100644 index c51c714d0b..0000000000 --- a/packages/medusa-payment-adyen/src/services/ideal-adyen.js +++ /dev/null @@ -1,62 +0,0 @@ -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 diff --git a/packages/medusa-payment-adyen/src/services/mobilepay-adyen.js b/packages/medusa-payment-adyen/src/services/mobilepay-adyen.js index 9df408e14d..3cf851cd51 100644 --- a/packages/medusa-payment-adyen/src/services/mobilepay-adyen.js +++ b/packages/medusa-payment-adyen/src/services/mobilepay-adyen.js @@ -15,7 +15,9 @@ class MobilePayAdyenService extends PaymentService { } async createPayment(data) { - return this.adyenService_.createPayment(data) + const raw = await this.adyenService_.createPayment(data) + raw.type = "mobilepay" + return raw } async authorizePayment(sessionData, context) { diff --git a/packages/medusa-payment-adyen/src/services/paypal-adyen.js b/packages/medusa-payment-adyen/src/services/paypal-adyen.js deleted file mode 100644 index e7aefa023f..0000000000 --- a/packages/medusa-payment-adyen/src/services/paypal-adyen.js +++ /dev/null @@ -1,58 +0,0 @@ -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 diff --git a/packages/medusa-payment-adyen/src/subscribers/adyen.js b/packages/medusa-payment-adyen/src/subscribers/adyen.js index 2c140d518e..66ef276ca9 100644 --- a/packages/medusa-payment-adyen/src/subscribers/adyen.js +++ b/packages/medusa-payment-adyen/src/subscribers/adyen.js @@ -105,7 +105,6 @@ class AdyenSubscriber { await this.paymentRepository_.save(updatedPayment) } catch (error) { - console.log(error) await this.manager_.transaction(async (manager) => { const session = { pspReference: notification.pspReference, diff --git a/packages/medusa/src/api/routes/store/carts/complete-cart.js b/packages/medusa/src/api/routes/store/carts/complete-cart.js index f3d02f9a61..e1997fcafc 100644 --- a/packages/medusa/src/api/routes/store/carts/complete-cart.js +++ b/packages/medusa/src/api/routes/store/carts/complete-cart.js @@ -1,7 +1,7 @@ import { MedusaError } from "medusa-core-utils" /** - * @oas [post] /carts/{id}/complete-cart + * @oas [post] /carts/{id}/complete * summary: "Complete a Cart" * operationId: "PostCartsCartComplete" * description: "Completes a cart. The following steps will be performed. Payment diff --git a/packages/medusa/src/api/routes/store/carts/index.js b/packages/medusa/src/api/routes/store/carts/index.js index 0ec18e79fe..42363bd12c 100644 --- a/packages/medusa/src/api/routes/store/carts/index.js +++ b/packages/medusa/src/api/routes/store/carts/index.js @@ -24,6 +24,12 @@ export default (app, container) => { route.post("/:id", middlewares.wrap(require("./update-cart").default)) + route.post( + "/:id/complete", + middlewares.wrap(require("./complete-cart").default) + ) + + // DEPRECATION route.post( "/:id/complete-cart", middlewares.wrap(require("./complete-cart").default) @@ -55,7 +61,7 @@ export default (app, container) => { ) route.post( - "/:id/payment-session/update", + "/:id/payment-sessions/:provider_id", middlewares.wrap(require("./update-payment-session").default) ) diff --git a/packages/medusa/src/api/routes/store/carts/update-payment-session.js b/packages/medusa/src/api/routes/store/carts/update-payment-session.js index e86ed96adb..7b9d31b92c 100644 --- a/packages/medusa/src/api/routes/store/carts/update-payment-session.js +++ b/packages/medusa/src/api/routes/store/carts/update-payment-session.js @@ -2,12 +2,13 @@ import { Validator, MedusaError } from "medusa-core-utils" import { defaultFields, defaultRelations } from "./" /** - * @oas [post] /carts/{id}/payment-session/update + * @oas [post] /carts/{id}/payment-sessions/{provider_id} * operationId: PostCartsCartPaymentSessionUpdate * summary: Update a Payment Session * description: "Updates a Payment Session with additional data." * parameters: * - (path) id=* {string} The id of the Cart. + * - (path) provider_id=* {string} The id of the payment provider. * - (body) provider_id=* {string} The id of the Payment Provider responsible for the Payment Session to update. * - (body) data=* {object} The data to update the payment session with. * tags: @@ -23,10 +24,10 @@ import { defaultFields, defaultRelations } from "./" * $ref: "#/components/schemas/cart" */ export default async (req, res) => { - const { id } = req.params + const { id, provider_id } = req.params const schema = Validator.object().keys({ - session: Validator.object().required(), + data: Validator.object().required(), }) const { value, error } = schema.validate(req.body) @@ -37,7 +38,8 @@ export default async (req, res) => { try { const cartService = req.scope.resolve("cartService") - await cartService.updatePaymentSession(id, value.session) + await cartService.setPaymentSession(id, provider_id) + await cartService.updatePaymentSession(id, value.data) const cart = await cartService.retrieve(id, { select: defaultFields, diff --git a/packages/medusa/src/services/order.js b/packages/medusa/src/services/order.js index 716c7d242a..1f082ebe46 100644 --- a/packages/medusa/src/services/order.js +++ b/packages/medusa/src/services/order.js @@ -461,9 +461,9 @@ class OrderService extends BaseService { ) } - const paymentStatus = await this.paymentProviderService_.getStatus( - payment - ) + const paymentStatus = await this.paymentProviderService_ + .withTransaction(manager) + .getStatus(payment) // If payment status is not authorized, we throw if (paymentStatus !== "authorized" && paymentStatus !== "succeeded") { diff --git a/packages/medusa/src/services/payment-provider.js b/packages/medusa/src/services/payment-provider.js index 4e153ce4bf..1b3eee462e 100644 --- a/packages/medusa/src/services/payment-provider.js +++ b/packages/medusa/src/services/payment-provider.js @@ -27,6 +27,7 @@ class PaymentProviderService extends BaseService { const cloned = new PaymentProviderService(this.container_) cloned.transactionManager_ = manager + cloned.manager_ = manager return cloned }