Skip to content

Commit

Permalink
feat(medusa,medusa-payment-stripe): Migrate Stripe to Abstract paymen…
Browse files Browse the repository at this point in the history
…t service (#1790)
  • Loading branch information
adrien2p committed Oct 4, 2022
1 parent 00959f7 commit df62e61
Show file tree
Hide file tree
Showing 19 changed files with 544 additions and 385 deletions.
6 changes: 6 additions & 0 deletions .changeset/nine-trainers-protect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"medusa-payment-stripe": patch
"@medusajs/medusa": patch
---

Migrate Stripe providers to the new AbstractPaymentService
3 changes: 3 additions & 0 deletions packages/medusa-payment-stripe/src/__mocks__/cart.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@ export const carts = {
}

export const CartServiceMock = {
withTransaction: function () {
return this
},
retrieve: jest.fn().mockImplementation((cartId) => {
if (cartId === IdMap.getId("fr-cart")) {
return Promise.resolve(carts.frCart)
Expand Down
3 changes: 3 additions & 0 deletions packages/medusa-payment-stripe/src/__mocks__/customer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { IdMap } from "medusa-test-utils"

export const CustomerServiceMock = {
withTransaction: function () {
return this
},
retrieve: jest.fn().mockImplementation((id) => {
if (id === IdMap.getId("lebron")) {
return Promise.resolve({
Expand Down
3 changes: 3 additions & 0 deletions packages/medusa-payment-stripe/src/__mocks__/totals.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
export const TotalsServiceMock = {
withTransaction: function () {
return this
},
getTotal: jest.fn(),
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export default (fn) => (...args) => fn(...args).catch(args[2])
export default (fn) =>
(...args) =>
fn(...args).catch(args[2])
24 changes: 10 additions & 14 deletions packages/medusa-payment-stripe/src/api/routes/hooks/stripe.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export default async (req, res) => {

const paymentIntent = event.data.object

const manager = req.scope.resolve("manager")
const cartService = req.scope.resolve("cartService")
const orderService = req.scope.resolve("orderService")

Expand All @@ -24,24 +25,19 @@ export default async (req, res) => {
switch (event.type) {
case "payment_intent.succeeded":
if (order && order.payment_status !== "captured") {
await orderService.capturePayment(order.id)
await manager.transaction(async (manager) => {
await orderService.withTransaction(manager).capturePayment(order.id)
})
}
break
//case "payment_intent.canceled":
// if (order) {
// await orderService.update(order._id, {
// status: "canceled",
// })
// }
// break
case "payment_intent.payment_failed":
// TODO: Not implemented yet
break
case "payment_intent.amount_capturable_updated":
if (!order) {
await cartService.setPaymentSession(cartId, "stripe")
await cartService.authorizePayment(cartId)
await orderService.createFromCart(cartId)
await manager.transaction(async (manager) => {
const cartServiceTx = cartService.withTransaction(manager)
await cartServiceTx.setPaymentSession(cartId, "stripe")
await cartServiceTx.authorizePayment(cartId)
await orderService.withTransaction(manager).createFromCart(cartId)
})
}
break
default:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { IdMap } from "medusa-test-utils"

export const StripeProviderServiceMock = {
withTransaction: function () {
return this
},
retrievePayment: jest.fn().mockImplementation((payData) => {
if (payData.id === "pi_123456789") {
return Promise.resolve({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { carts } from "../../__mocks__/cart"
import { TotalsServiceMock } from "../../__mocks__/totals"

const RegionServiceMock = {
withTransaction: function () {
return this
},
retrieve: jest.fn().mockReturnValue(Promise.resolve({})),
}

Expand Down
141 changes: 83 additions & 58 deletions packages/medusa-payment-stripe/src/services/stripe-bancontact.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
import _ from "lodash"
import Stripe from "stripe"
import { PaymentService } from "medusa-interfaces"
import { AbstractPaymentService, PaymentSessionData } from "@medusajs/medusa"

class BancontactProviderService extends PaymentService {
class BancontactProviderService extends AbstractPaymentService {
static identifier = "stripe-bancontact"

constructor(
{ stripeProviderService, customerService, totalsService, regionService },
{
stripeProviderService,
customerService,
totalsService,
regionService,
manager,
},
options
) {
super()
super(
{
stripeProviderService,
customerService,
totalsService,
regionService,
manager,
},
options
)

/**
* Required Stripe options:
Expand All @@ -36,22 +50,25 @@ class BancontactProviderService extends PaymentService {

/** @private @const {TotalsService} */
this.totalsService_ = totalsService

/** @private @const {EntityManager} */
this.manager_ = manager
}

/**
* Fetches Stripe payment intent. Check its status and returns the
* corresponding Medusa status.
* @param {object} paymentData - payment method data from cart
* @returns {string} the status of the payment intent
* @param {PaymentSessionData} paymentSessionData - payment method data from cart
* @return {Promise<PaymentSessionStatus>} the status of the payment intent
*/
async getStatus(paymentData) {
return await this.stripeProviderService_.getStatus(paymentData)
async getStatus(paymentSessionData) {
return await this.stripeProviderService_.getStatus(paymentSessionData)
}

/**
* Fetches a customers saved payment methods if registered in Stripe.
* @param {object} customer - customer to fetch saved cards for
* @returns {Promise<Array<object>>} saved payments methods
* @return {Promise<Data[]>} saved payments methods
*/
async retrieveSavedMethods(customer) {
return Promise.resolve([])
Expand All @@ -60,7 +77,7 @@ class BancontactProviderService extends PaymentService {
/**
* Fetches a Stripe customer
* @param {string} customerId - Stripe customer id
* @returns {Promise<object>} Stripe customer
* @return {Promise<object>} Stripe customer
*/
async retrieveCustomer(customerId) {
return await this.stripeProviderService_.retrieveCustomer(customerId)
Expand All @@ -69,36 +86,45 @@ class BancontactProviderService extends PaymentService {
/**
* Creates a Stripe customer using a Medusa customer.
* @param {object} customer - Customer data from Medusa
* @returns {Promise<object>} Stripe customer
* @return {Promise<object>} Stripe customer
*/
async createCustomer(customer) {
return await this.stripeProviderService_.createCustomer(customer)
return await this.stripeProviderService_
.withTransaction(this.manager_)
.createCustomer(customer)
}

/**
* Creates a Stripe payment intent.
* If customer is not registered in Stripe, we do so.
* @param {object} cart - cart to create a payment for
* @returns {object} Stripe payment intent
* @param {Cart} cart - cart to create a payment for
* @return {Promise<PaymentSessionData>} Stripe payment intent
*/
async createPayment(cart) {
const { customer_id, region_id, email } = cart
const region = await this.regionService_.retrieve(region_id)
const region = await this.regionService_
.withTransaction(this.manager_)
.retrieve(region_id)
const { currency_code } = region

const amount = await this.totalsService_.getTotal(cart)
const amount = await this.totalsService_
.withTransaction(this.manager_)
.getTotal(cart)

const intentRequest = {
amount: Math.round(amount),
description: cart?.context?.payment_description ?? this.options_?.payment_description,
description:
cart?.context?.payment_description ?? this.options?.payment_description,
currency: currency_code,
payment_method_types: ["bancontact"],
capture_method: "automatic",
metadata: { cart_id: `${cart.id}` },
}

if (customer_id) {
const customer = await this.customerService_.retrieve(customer_id)
const customer = await this.customerService_
.withTransaction(this.manager_)
.retrieve(customer_id)

if (customer.metadata?.stripe_id) {
intentRequest.customer = customer.metadata.stripe_id
Expand All @@ -118,70 +144,69 @@ class BancontactProviderService extends PaymentService {
intentRequest.customer = stripeCustomer.id
}

const paymentIntent = await this.stripe_.paymentIntents.create(
intentRequest
)

return paymentIntent
return await this.stripe_.paymentIntents.create(intentRequest)
}

/**
* Retrieves Stripe payment intent.
* @param {object} data - the data of the payment to retrieve
* @returns {Promise<object>} Stripe payment intent
* @param {PaymentData} paymentData - the data of the payment to retrieve
* @return {Promise<Data>} Stripe payment intent
*/
async retrievePayment(data) {
return await this.stripeProviderService_.retrievePayment(data)
async retrievePayment(paymentData) {
return await this.stripeProviderService_.retrievePayment(paymentData)
}

/**
* Gets a Stripe payment intent and returns it.
* @param {object} sessionData - the data of the payment to retrieve
* @returns {Promise<object>} Stripe payment intent
* @param {PaymentSession} paymentSession - the data of the payment to retrieve
* @return {Promise<PaymentData>} Stripe payment intent
*/
async getPaymentData(sessionData) {
return await this.stripeProviderService_.getPaymentData(sessionData)
async getPaymentData(paymentSession) {
return await this.stripeProviderService_.getPaymentData(paymentSession)
}

/**
* Authorizes Stripe payment intent by simply returning
* the status for the payment intent in use.
* @param {object} sessionData - payment session data
* @param {PaymentSession} paymentSession - payment session data
* @param {object} context - properties relevant to current context
* @returns {Promise<{ status: string, data: object }>} result with data and status
* @return {Promise<{data: PaymentSessionData; status: PaymentSessionStatus}>} result with data and status
*/
async authorizePayment(sessionData, context = {}) {
async authorizePayment(paymentSession, context = {}) {
return await this.stripeProviderService_.authorizePayment(
sessionData,
paymentSession,
context
)
}

async updatePaymentData(sessionData, update) {
async updatePaymentData(paymentSessionData, data) {
return await this.stripeProviderService_.updatePaymentData(
sessionData,
update
paymentSessionData,
data
)
}

/**
* Updates Stripe payment intent.
* @param {object} sessionData - payment session data.
* @param {object} update - objec to update intent with
* @returns {object} Stripe payment intent
* @param {PaymentSessionData} paymentSessionData - payment session data.
* @param {Cart} cart
* @return {Promise<PaymentSessionData>} Stripe payment intent
*/
async updatePayment(sessionData, cart) {
async updatePayment(paymentSessionData, cart) {
try {
const stripeId = cart.customer?.metadata?.stripe_id || undefined

if (stripeId !== sessionData.customer) {
if (stripeId !== paymentSessionData.customer) {
return this.createPayment(cart)
} else {
if (cart.total && sessionData.amount === Math.round(cart.total)) {
if (
cart.total &&
paymentSessionData.amount === Math.round(cart.total)
) {
return sessionData
}

return this.stripe_.paymentIntents.update(sessionData.id, {
return this.stripe_.paymentIntents.update(paymentSessionData.id, {
amount: Math.round(cart.total),
})
}
Expand All @@ -190,15 +215,15 @@ class BancontactProviderService extends PaymentService {
}
}

async deletePayment(payment) {
return await this.stripeProviderService_.deletePayment(payment)
async deletePayment(paymentSession) {
return await this.stripeProviderService_.deletePayment(paymentSession)
}

/**
* Updates customer of Stripe payment intent.
* @param {string} paymentIntentId - id of payment intent to update
* @param {string} customerId - id of new Stripe customer
* @returns {object} Stripe payment intent
* @return {object} Stripe payment intent
*/
async updatePaymentIntentCustomer(paymentIntentId, customerId) {
return await this.stripeProviderService_.updatePaymentIntentCustomer(
Expand All @@ -209,30 +234,30 @@ class BancontactProviderService extends PaymentService {

/**
* Captures payment for Stripe payment intent.
* @param {object} paymentData - payment method data from cart
* @returns {object} Stripe payment intent
* @param {Payment} payment - payment method data from cart
* @return {Promise<PaymentData>} Stripe payment intent
*/
async capturePayment(payment) {
return await this.stripeProviderService_.capturePayment(payment)
}

/**
* Refunds payment for Stripe payment intent.
* @param {object} paymentData - payment method data from cart
* @param {number} amountToRefund - amount to refund
* @returns {string} refunded payment intent
* @param {Payment} payment - payment method data from cart
* @param {number} refundAmount - amount to refund
* @return {Promise<PaymentData>} refunded payment intent
*/
async refundPayment(payment, amountToRefund) {
async refundPayment(payment, refundAmount) {
return await this.stripeProviderService_.refundPayment(
payment,
amountToRefund
refundAmount
)
}

/**
* Cancels payment for Stripe payment intent.
* @param {object} paymentData - payment method data from cart
* @returns {object} canceled payment intent
* @param {Payment} payment - payment method data from cart
* @return {Promise<PaymentData>} canceled payment intent
*/
async cancelPayment(payment) {
return await this.stripeProviderService_.cancelPayment(payment)
Expand Down

0 comments on commit df62e61

Please sign in to comment.