feat(medusa): manage payment sessions from payment collection (#2402)

* feat: manage payment sessions from payment collection
This commit is contained in:
Carlos R. L. Rodrigues authored and GitHub committed 2022-11-02 13:34:29 -03:00
1 parent a014e3623c
commit c3f8297599
28 files changed
+2002 -1315

No files matched your search

@@ -10,40 +10,53 @@ export default async (req, res) => {
return
}
const paymentIntent = event.data.object
const manager = req.scope.resolve("manager")
const cartService = req.scope.resolve("cartService")
const orderService = req.scope.resolve("orderService")
const cartId = paymentIntent.metadata.cart_id
const order = await orderService
.retrieveByCartId(cartId)
.catch(() => undefined)
// handle payment intent events
switch (event.type) {
case "payment_intent.succeeded":
if (order && order.payment_status !== "captured") {
await manager.transaction(async (manager) => {
await orderService.withTransaction(manager).capturePayment(order.id)
})
}
break
case "payment_intent.amount_capturable_updated":
if (!order) {
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:
res.sendStatus(204)
return
function isPaymentCollection(id) {
return id && id.startsWith("paycol")
}
res.sendStatus(200)
async function handleCartPayments(event, req, res, cartId) {
const manager = req.scope.resolve("manager")
const cartService = req.scope.resolve("cartService")
const orderService = req.scope.resolve("orderService")
const order = await orderService
.retrieveByCartId(cartId)
.catch(() => undefined)
// handle payment intent events
switch (event.type) {
case "payment_intent.succeeded":
if (order && order.payment_status !== "captured") {
await manager.transaction(async (manager) => {
await orderService.withTransaction(manager).capturePayment(order.id)
})
}
break
case "payment_intent.amount_capturable_updated":
if (!order) {
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:
res.sendStatus(204)
return
}
res.sendStatus(200)
}
const paymentIntent = event.data.object
const cartId = paymentIntent.metadata.cart_id
const resourceId = paymentIntent.metadata.resource_id
if (isPaymentCollection(resourceId)) {
// TODO: handle payment collection
} else {
await handleCartPayments(event, req, res, resourceId ?? cartId)
}
}
@@ -0,0 +1,235 @@
import Stripe from "stripe"
import { AbstractPaymentService, PaymentSessionData } from "@medusajs/medusa"
class StripeBase extends AbstractPaymentService {
static identifier = null
constructor(
{
stripeProviderService,
customerService,
totalsService,
regionService,
manager,
},
options,
paymentMethodTypes
) {
super(
{
stripeProviderService,
customerService,
totalsService,
regionService,
manager,
},
options
)
/** @private @const {string[]} */
this.paymentMethodTypes = paymentMethodTypes
/**
* Required Stripe options:
* {
* api_key: "stripe_secret_key", REQUIRED
* webhook_secret: "stripe_webhook_secret", REQUIRED
* // Use this flag to capture payment immediately (default is false)
* capture: true
* }
*/
this.options_ = options
/** @private @const {Stripe} */
this.stripe_ = Stripe(options.api_key)
/** @private @const {CustomerService} */
this.stripeProviderService_ = stripeProviderService
/** @private @const {CustomerService} */
this.customerService_ = customerService
/** @private @const {RegionService} */
this.regionService_ = regionService
/** @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 {PaymentSessionData} paymentSessionData - payment method data from cart
* @return {Promise<PaymentSessionStatus>} the status of the payment intent
*/
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
* @return {Promise<Data[]>} saved payments methods
*/
async retrieveSavedMethods(customer) {
return Promise.resolve([])
}
/**
* Fetches a Stripe customer
* @param {string} customerId - Stripe customer id
* @return {Promise<object>} Stripe customer
*/
async retrieveCustomer(customerId) {
return await this.stripeProviderService_.retrieveCustomer(customerId)
}
/**
* Creates a Stripe customer using a Medusa customer.
* @param {object} customer - Customer data from Medusa
* @return {Promise<object>} Stripe customer
*/
async 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 {Cart} cart - cart to create a payment for
* @return {Promise<PaymentSessionData>} Stripe payment intent
*/
async createPayment(cart) {
const intentRequest = {
payment_method_types: this.paymentMethodTypes,
capture_method: "automatic",
}
return await this.stripeProviderService_.createPayment(cart, intentRequest)
}
async createPaymentNew(paymentInput) {
const intentRequest = {
payment_method_types: this.paymentMethodTypes,
capture_method: "automatic",
}
return await this.stripeProviderService_.createPaymentNew(
paymentInput,
intentRequest
)
}
/**
* Retrieves Stripe payment intent.
* @param {PaymentData} paymentData - the data of the payment to retrieve
* @return {Promise<Data>} Stripe payment intent
*/
async retrievePayment(paymentData) {
return await this.stripeProviderService_.retrievePayment(paymentData)
}
/**
* Gets a Stripe payment intent and returns it.
* @param {PaymentSession} paymentSession - the data of the payment to retrieve
* @return {Promise<PaymentData>} Stripe payment intent
*/
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 {PaymentSession} paymentSession - payment session data
* @param {object} context - properties relevant to current context
* @return {Promise<{data: PaymentSessionData; status: PaymentSessionStatus}>} result with data and status
*/
async authorizePayment(paymentSession, context = {}) {
return await this.stripeProviderService_.authorizePayment(
paymentSession,
context
)
}
async updatePaymentData(paymentSessionData, data) {
return await this.stripeProviderService_.updatePaymentData(
paymentSessionData,
data
)
}
/**
* Updates Stripe payment intent.
* @param {PaymentSessionData} paymentSessionData - payment session data.
* @param {Cart} cart
* @return {Promise<PaymentSessionData>} Stripe payment intent
*/
async updatePayment(paymentSessionData, cart) {
return await this.stripeProviderService_.updatePayment(
paymentSessionData,
cart
)
}
async updatePaymentNew(paymentSessionData, paymentInput) {
return await this.stripeProviderService_.updatePaymentNew(
paymentSessionData,
paymentInput
)
}
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
* @return {object} Stripe payment intent
*/
async updatePaymentIntentCustomer(paymentIntentId, customerId) {
return await this.stripeProviderService_.updatePaymentIntentCustomer(
paymentIntentId,
customerId
)
}
/**
* Captures payment for 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 {Payment} payment - payment method data from cart
* @param {number} refundAmount - amount to refund
* @return {Promise<PaymentData>} refunded payment intent
*/
async refundPayment(payment, refundAmount) {
return await this.stripeProviderService_.refundPayment(
payment,
refundAmount
)
}
/**
* Cancels payment for Stripe 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)
}
}
export default StripeBase
@@ -1,7 +1,6 @@
import Stripe from "stripe"
import { AbstractPaymentService, PaymentSessionData } from "@medusajs/medusa"
import StripeBase from "../helpers/stripe-base"
class BancontactProviderService extends AbstractPaymentService {
class BancontactProviderService extends StripeBase {
static identifier = "stripe-bancontact"
constructor(
@@ -22,245 +21,9 @@ class BancontactProviderService extends AbstractPaymentService {
regionService,
manager,
},
options
options,
["bancontact"]
)
/**
* Required Stripe options:
* {
* api_key: "stripe_secret_key", REQUIRED
* webhook_secret: "stripe_webhook_secret", REQUIRED
* // Use this flag to capture payment immediately (default is false)
* capture: true
* }
*/
this.options_ = options
/** @private @const {Stripe} */
this.stripe_ = Stripe(options.api_key)
/** @private @const {CustomerService} */
this.stripeProviderService_ = stripeProviderService
/** @private @const {CustomerService} */
this.customerService_ = customerService
/** @private @const {RegionService} */
this.regionService_ = regionService
/** @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 {PaymentSessionData} paymentSessionData - payment method data from cart
* @return {Promise<PaymentSessionStatus>} the status of the payment intent
*/
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
* @return {Promise<Data[]>} saved payments methods
*/
async retrieveSavedMethods(customer) {
return Promise.resolve([])
}
/**
* Fetches a Stripe customer
* @param {string} customerId - Stripe customer id
* @return {Promise<object>} Stripe customer
*/
async retrieveCustomer(customerId) {
return await this.stripeProviderService_.retrieveCustomer(customerId)
}
/**
* Creates a Stripe customer using a Medusa customer.
* @param {object} customer - Customer data from Medusa
* @return {Promise<object>} Stripe customer
*/
async 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 {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_
.withTransaction(this.manager_)
.retrieve(region_id)
const { currency_code } = region
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,
currency: currency_code,
payment_method_types: ["bancontact"],
capture_method: "automatic",
metadata: { cart_id: `${cart.id}` },
}
if (customer_id) {
const customer = await this.customerService_
.withTransaction(this.manager_)
.retrieve(customer_id)
if (customer.metadata?.stripe_id) {
intentRequest.customer = customer.metadata.stripe_id
} else {
const stripeCustomer = await this.createCustomer({
email,
id: customer_id,
})
intentRequest.customer = stripeCustomer.id
}
} else {
const stripeCustomer = await this.createCustomer({
email,
})
intentRequest.customer = stripeCustomer.id
}
return await this.stripe_.paymentIntents.create(intentRequest)
}
/**
* Retrieves Stripe payment intent.
* @param {PaymentData} paymentData - the data of the payment to retrieve
* @return {Promise<Data>} Stripe payment intent
*/
async retrievePayment(paymentData) {
return await this.stripeProviderService_.retrievePayment(paymentData)
}
/**
* Gets a Stripe payment intent and returns it.
* @param {PaymentSession} paymentSession - the data of the payment to retrieve
* @return {Promise<PaymentData>} Stripe payment intent
*/
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 {PaymentSession} paymentSession - payment session data
* @param {object} context - properties relevant to current context
* @return {Promise<{data: PaymentSessionData; status: PaymentSessionStatus}>} result with data and status
*/
async authorizePayment(paymentSession, context = {}) {
return await this.stripeProviderService_.authorizePayment(
paymentSession,
context
)
}
async updatePaymentData(paymentSessionData, data) {
return await this.stripeProviderService_.updatePaymentData(
paymentSessionData,
data
)
}
/**
* Updates Stripe payment intent.
* @param {PaymentSessionData} paymentSessionData - payment session data.
* @param {Cart} cart
* @return {Promise<PaymentSessionData>} Stripe payment intent
*/
async updatePayment(paymentSessionData, cart) {
try {
const stripeId = cart.customer?.metadata?.stripe_id || undefined
if (stripeId !== paymentSessionData.customer) {
return this.createPayment(cart)
} else {
if (
cart.total &&
paymentSessionData.amount === Math.round(cart.total)
) {
return sessionData
}
return this.stripe_.paymentIntents.update(paymentSessionData.id, {
amount: Math.round(cart.total),
})
}
} catch (error) {
throw error
}
}
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
* @return {object} Stripe payment intent
*/
async updatePaymentIntentCustomer(paymentIntentId, customerId) {
return await this.stripeProviderService_.updatePaymentIntentCustomer(
paymentIntentId,
customerId
)
}
/**
* Captures payment for 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 {Payment} payment - payment method data from cart
* @param {number} refundAmount - amount to refund
* @return {Promise<PaymentData>} refunded payment intent
*/
async refundPayment(payment, refundAmount) {
return await this.stripeProviderService_.refundPayment(
payment,
refundAmount
)
}
/**
* Cancels payment for Stripe 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)
}
}
@@ -1,7 +1,6 @@
import Stripe from "stripe"
import { AbstractPaymentService, PaymentSessionStatus } from "@medusajs/medusa"
import StripeBase from "../helpers/stripe-base"
class BlikProviderService extends AbstractPaymentService {
class BlikProviderService extends StripeBase {
static identifier = "stripe-blik"
constructor(
@@ -22,242 +21,9 @@ class BlikProviderService extends AbstractPaymentService {
regionService,
manager,
},
options
options,
["blik"]
)
/**
* Required Stripe options:
* {
* api_key: "stripe_secret_key", REQUIRED
* webhook_secret: "stripe_webhook_secret", REQUIRED
* // Use this flag to capture payment immediately (default is false)
* capture: true
* }
*/
this.options_ = options
/** @private @const {Stripe} */
this.stripe_ = Stripe(options.api_key)
/** @private @const {CustomerService} */
this.stripeProviderService_ = stripeProviderService
/** @private @const {CustomerService} */
this.customerService_ = customerService
/** @private @const {RegionService} */
this.regionService_ = regionService
/** @private @const {TotalsService} */
this.totalsService_ = totalsService
this.manager_ = manager
}
/**
* Fetches Stripe payment intent. Check its status and returns the
* corresponding Medusa status.
* @param {PaymentSessionData} paymentSessionData - payment method data from cart
* @return {Promise<PaymentSessionStatus>} the status of the payment intent
*/
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
* @return {Promise<Data[]>} saved payments methods
*/
async retrieveSavedMethods(customer) {
return Promise.resolve([])
}
/**
* Fetches a Stripe customer
* @param {string} customerId - Stripe customer id
* @return {Promise<object>} Stripe customer
*/
async retrieveCustomer(customerId) {
return await this.stripeProviderService_.retrieveCustomer(customerId)
}
/**
* Creates a Stripe customer using a Medusa customer.
* @param {object} customer - Customer data from Medusa
* @return {Promise<object>} Stripe customer
*/
async 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 {Cart} cart - cart to create a payment for
* @returns {PaymentSessionData} Stripe payment intent
*/
async createPayment(cart) {
const { customer_id, region_id, email } = cart
const region = await this.regionService_
.withTransaction(this.manager_)
.retrieve(region_id)
const { currency_code } = region
const amount = await this.totalsService_
.withTransaction(this.manager_)
.getTotal(cart)
const intentRequest = {
amount: Math.round(amount),
currency: currency_code,
payment_method_types: ["blik"],
capture_method: "automatic",
metadata: { cart_id: `${cart.id}` },
}
if (customer_id) {
const customer = await this.customerService_
.withTransaction(this.manager_)
.retrieve(customer_id)
if (customer.metadata?.stripe_id) {
intentRequest.customer = customer.metadata.stripe_id
} else {
const stripeCustomer = await this.createCustomer({
email,
id: customer_id,
})
intentRequest.customer = stripeCustomer.id
}
} else {
const stripeCustomer = await this.createCustomer({
email,
})
intentRequest.customer = stripeCustomer.id
}
return await this.stripe_.paymentIntents.create(intentRequest)
}
/**
* Retrieves Stripe payment intent.
* @param {PaymentData} paymentData - the data of the payment to retrieve
* @return {Promise<Data>} Stripe payment intent
*/
async retrievePayment(paymentData) {
return await this.stripeProviderService_.retrievePayment(paymentData)
}
/**
* Gets a Stripe payment intent and returns it.
* @param {PaymentSession} paymentSession - the data of the payment to retrieve
* @return {Promise<PaymentData>} Stripe payment intent
*/
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 {PaymentSession} paymentSession - payment session data
* @param {object} context - properties relevant to current context
* @return {Promise<{data: PaymentSessionData; status: PaymentSessionStatus}>} result with data and status
*/
async authorizePayment(paymentSession, context = {}) {
return await this.stripeProviderService_.authorizePayment(
paymentSession,
context
)
}
async updatePaymentData(paymentSessionData, data) {
return await this.stripeProviderService_.updatePaymentData(
paymentSessionData,
data
)
}
/**
* Updates Stripe payment intent.
* @param {PaymentSessionData} paymentSessionData - payment session data.
* @param {Cart} cart
* @return {Promise<PaymentSessionData>} Stripe payment intent
*/
async updatePayment(paymentSessionData, cart) {
try {
const stripeId = cart.customer?.metadata?.stripe_id || undefined
if (stripeId !== paymentSessionData.customer) {
return this.createPayment(cart)
} else {
if (
cart.total &&
paymentSessionData.amount === Math.round(cart.total)
) {
return sessionData
}
return this.stripe_.paymentIntents.update(paymentSessionData.id, {
amount: Math.round(cart.total),
})
}
} catch (error) {
throw error
}
}
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
* @return {object} Stripe payment intent
*/
async updatePaymentIntentCustomer(paymentIntentId, customerId) {
return await this.stripeProviderService_.updatePaymentIntentCustomer(
paymentIntentId,
customerId
)
}
/**
* Captures payment for 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 {Payment} payment - payment method data from cart
* @param {number} refundAmount - amount to refund
* @return {Promise<PaymentData>} refunded payment intent
*/
async refundPayment(payment, refundAmount) {
return await this.stripeProviderService_.refundPayment(
payment,
refundAmount
)
}
/**
* Cancels payment for Stripe 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)
}
}
@@ -1,7 +1,6 @@
import Stripe from "stripe"
import { AbstractPaymentService, PaymentSessionStatus } from "@medusajs/medusa"
import StripeBase from "../helpers/stripe-base"
class GiropayProviderService extends AbstractPaymentService {
class GiropayProviderService extends StripeBase {
static identifier = "stripe-giropay"
constructor(
@@ -22,245 +21,9 @@ class GiropayProviderService extends AbstractPaymentService {
regionService,
manager,
},
options
options,
["giropay"]
)
/**
* Required Stripe options:
* {
* api_key: "stripe_secret_key", REQUIRED
* webhook_secret: "stripe_webhook_secret", REQUIRED
* // Use this flag to capture payment immediately (default is false)
* capture: true
* }
*/
this.options_ = options
/** @private @const {Stripe} */
this.stripe_ = Stripe(options.api_key)
/** @private @const {CustomerService} */
this.stripeProviderService_ = stripeProviderService
/** @private @const {CustomerService} */
this.customerService_ = customerService
/** @private @const {RegionService} */
this.regionService_ = regionService
/** @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 {PaymentSessionData} paymentSessionData - payment method data from cart
* @return {Promise<PaymentSessionStatus>} the status of the payment intent
*/
async getStatus(paymentSessionData) {
return await this.stripeProviderService_.getStatus(paymentSessionData)
}
/**
* Fetches a customers saved payment methods if registered in Stripe.
* @param {Customer} customer - customer to fetch saved cards for
* @return {Promise<Data[]>} saved payments methods
*/
async retrieveSavedMethods(customer) {
return Promise.resolve([])
}
/**
* Fetches a Stripe customer
* @param {string} customerId - Stripe customer id
* @return {Promise<object>} Stripe customer
*/
async retrieveCustomer(customerId) {
return await this.stripeProviderService_.retrieveCustomer(customerId)
}
/**
* Creates a Stripe customer using a Medusa customer.
* @param {object} customer - Customer data from Medusa
* @return {Promise<object>} Stripe customer
*/
async 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 {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_
.withTransaction(this.manager_)
.retrieve(region_id)
const { currency_code } = region
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,
currency: currency_code,
payment_method_types: ["giropay"],
capture_method: "automatic",
metadata: { cart_id: `${cart.id}` },
}
if (customer_id) {
const customer = await this.customerService_
.withTransaction(this.manager_)
.retrieve(customer_id)
if (customer.metadata?.stripe_id) {
intentRequest.customer = customer.metadata.stripe_id
} else {
const stripeCustomer = await this.createCustomer({
email,
id: customer_id,
})
intentRequest.customer = stripeCustomer.id
}
} else {
const stripeCustomer = await this.createCustomer({
email,
})
intentRequest.customer = stripeCustomer.id
}
return await this.stripe_.paymentIntents.create(intentRequest)
}
/**
* Retrieves Stripe payment intent.
* @param {PaymentData} paymentData - the data of the payment to retrieve
* @return {Promise<Data>} Stripe payment intent
*/
async retrievePayment(paymentData) {
return await this.stripeProviderService_.retrievePayment(paymentData)
}
/**
* Gets a Stripe payment intent and returns it.
* @param {PaymentSession} paymentSession - the data of the payment to retrieve
* @return {Promise<PaymentData>} Stripe payment intent
*/
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 {PaymentSession} paymentSession - payment session data
* @param {Data} context - properties relevant to current context
* @return {Promise<{data: PaymentSessionData; status: PaymentSessionStatus}>} result with data and status
*/
async authorizePayment(paymentSession, context = {}) {
return await this.stripeProviderService_.authorizePayment(
paymentSession,
context
)
}
async updatePaymentData(paymentSessionData, data) {
return await this.stripeProviderService_.updatePaymentData(
paymentSessionData,
data
)
}
/**
* Updates Stripe payment intent.
* @param {PaymentSessionData} paymentSessionData - payment session data.
* @param {Cart} cart
* @return {Promise<PaymentSessionData>} Stripe payment intent
*/
async updatePayment(paymentSessionData, cart) {
try {
const stripeId = cart.customer?.metadata?.stripe_id || undefined
if (stripeId !== paymentSessionData.customer) {
return this.createPayment(cart)
} else {
if (
cart.total &&
paymentSessionData.amount === Math.round(cart.total)
) {
return paymentSessionData
}
return this.stripe_.paymentIntents.update(paymentSessionData.id, {
amount: Math.round(cart.total),
})
}
} catch (error) {
throw error
}
}
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
* @return {object} Stripe payment intent
*/
async updatePaymentIntentCustomer(paymentIntentId, customerId) {
return await this.stripeProviderService_.updatePaymentIntentCustomer(
paymentIntentId,
customerId
)
}
/**
* Captures payment for 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 {Payment} payment - payment method data from cart
* @param {number} refundAmount - amount to refund
* @return {Promise<PaymentData>} refunded payment intent
*/
async refundPayment(payment, refundAmount) {
return await this.stripeProviderService_.refundPayment(
payment,
refundAmount
)
}
/**
* Cancels payment for Stripe 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)
}
}
@@ -1,7 +1,6 @@
import Stripe from "stripe"
import { PaymentService } from "medusa-interfaces"
import StripeBase from "../helpers/stripe-base"
class IdealProviderService extends PaymentService {
class IdealProviderService extends StripeBase {
static identifier = "stripe-ideal"
constructor(
@@ -22,242 +21,9 @@ class IdealProviderService extends PaymentService {
regionService,
manager,
},
options
options,
["ideal"]
)
/**
* Required Stripe options:
* {
* api_key: "stripe_secret_key", REQUIRED
* webhook_secret: "stripe_webhook_secret", REQUIRED
* // Use this flag to capture payment immediately (default is false)
* capture: true
* }
*/
this.options_ = options
/** @private @const {Stripe} */
this.stripe_ = Stripe(options.api_key)
/** @private @const {CustomerService} */
this.stripeProviderService_ = stripeProviderService
/** @private @const {CustomerService} */
this.customerService_ = customerService
/** @private @const {RegionService} */
this.regionService_ = regionService
/** @private @const {TotalsService} */
this.totalsService_ = totalsService
this.manager_ = manager
}
/**
* Fetches Stripe payment intent. Check its status and returns the
* corresponding Medusa status.
* @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)
}
/**
* Fetches a customers saved payment methods if registered in Stripe.
* @param {object} customer - customer to fetch saved cards for
* @return {Promise<Data[]>} saved payments methods
*/
async retrieveSavedMethods(customer) {
return Promise.resolve([])
}
/**
* Fetches a Stripe customer
* @param {string} customerId - Stripe customer id
* @return {Promise<object>} Stripe customer
*/
async retrieveCustomer(customerId) {
return await this.stripeProviderService_.retrieveCustomer(customerId)
}
/**
* Creates a Stripe customer using a Medusa customer.
* @param {object} customer - Customer data from Medusa
* @return {Promise<object>} Stripe customer
*/
async 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 {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_
.withTransaction(this.manager_)
.retrieve(region_id)
const { currency_code } = region
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,
currency: currency_code,
payment_method_types: ["ideal"],
capture_method: "automatic",
metadata: { cart_id: `${cart.id}` },
}
if (customer_id) {
const customer = await this.customerService_
.withTransaction(this.manager_)
.retrieve(customer_id)
if (customer.metadata?.stripe_id) {
intentRequest.customer = customer.metadata.stripe_id
} else {
const stripeCustomer = await this.createCustomer({
email,
id: customer_id,
})
intentRequest.customer = stripeCustomer.id
}
} else {
const stripeCustomer = await this.createCustomer({
email,
})
intentRequest.customer = stripeCustomer.id
}
return await this.stripe_.paymentIntents.create(intentRequest)
}
/**
* Retrieves Stripe payment intent.
* @param {PaymentData} paymentData - the data of the payment to retrieve
* @return {Promise<Data>} Stripe payment intent
*/
async retrievePayment(paymentData) {
return await this.stripeProviderService_.retrievePayment(paymentData)
}
/**
* Gets a Stripe payment intent and returns it.
* @param {PaymentSession} paymentSession - the data of the payment to retrieve
* @return {Promise<PaymentData>} Stripe payment intent
*/
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 {PaymentSession} paymentSession - payment session data
* @param {object} context - properties relevant to current context
* @return {Promise<{data: PaymentSessionData; status: PaymentSessionStatus}>} result with data and status
*/
async authorizePayment(paymentSession, context = {}) {
return await this.stripeProviderService_.authorizePayment(
paymentSession,
context
)
}
async updatePaymentData(paymentSessionData, data) {
return await this.stripeProviderService_.updatePaymentData(
paymentSessionData,
data
)
}
/**
* Updates Stripe payment intent.
* @param {PaymentSessionData} paymentSessionData - payment session data.
* @param {Cart} cart
* @return {Promise<PaymentSessionData>} Stripe payment intent
*/
async updatePayment(sessionData, cart) {
try {
const stripeId = cart.customer?.metadata?.stripe_id || undefined
if (stripeId !== sessionData.customer) {
return this.createPayment(cart)
} else {
if (cart.total && sessionData.amount === Math.round(cart.total)) {
return sessionData
}
return this.stripe_.paymentIntents.update(sessionData.id, {
amount: Math.round(cart.total),
})
}
} catch (error) {
throw error
}
}
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
* @return {object} Stripe payment intent
*/
async updatePaymentIntentCustomer(paymentIntentId, customerId) {
return await this.stripeProviderService_.updatePaymentIntentCustomer(
paymentIntentId,
customerId
)
}
/**
* Captures payment for 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 {Payment} payment - payment method data from cart
* @param {number} refundAmount - amount to refund
* @return {Promise<PaymentData>} refunded payment intent
*/
async refundPayment(payment, refundAmount) {
return await this.stripeProviderService_.refundPayment(
payment,
refundAmount
)
}
/**
* Cancels payment for Stripe 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)
}
}
@@ -129,7 +129,7 @@ class StripeProviderService extends AbstractPaymentService {
* @param {Cart} cart - cart to create a payment for
* @return {Promise<PaymentSessionData>} Stripe payment intent
*/
async createPayment(cart) {
async createPayment(cart, intentRequestData = {}) {
const { customer_id, region_id, email } = cart
const { currency_code } = await this.regionService_
.withTransaction(this.manager_)
@@ -143,9 +143,10 @@ class StripeProviderService extends AbstractPaymentService {
this.options_?.payment_description,
amount: Math.round(amount),
currency: currency_code,
metadata: { cart_id: `${cart.id}` },
setup_future_usage: "on_session",
capture_method: this.options_.capture ? "automatic" : "manual",
metadata: { cart_id: `${cart.id}` },
...intentRequestData,
}
if (this.options_?.automatic_payment_methods) {
@@ -178,6 +179,44 @@ class StripeProviderService extends AbstractPaymentService {
return await this.stripe_.paymentIntents.create(intentRequest)
}
async createPaymentNew(paymentInput, intentRequestData = {}) {
const { customer, currency_code, amount, resource_id, cart } = paymentInput
const { id: customer_id, email } = customer
let intentRequest = {
description:
cart?.context?.payment_description ??
this.options_?.payment_description,
amount: Math.round(amount),
currency: currency_code,
metadata: { resource_id },
setup_future_usage: "on_session",
capture_method: this.options_.capture ? "automatic" : "manual",
...intentRequestData,
}
if (customer_id) {
if (customer.metadata?.stripe_id) {
intentRequest.customer = customer.metadata.stripe_id
} else {
const stripeCustomer = await this.createCustomer({
email,
id: customer_id,
})
intentRequest.customer = stripeCustomer.id
}
} else {
const stripeCustomer = await this.createCustomer({
email,
})
intentRequest.customer = stripeCustomer.id
}
return await this.stripe_.paymentIntents.create(intentRequest)
}
/**
* Retrieves Stripe payment intent.
* @param {PaymentData} paymentData - the data of the payment to retrieve
@@ -213,7 +252,6 @@ class StripeProviderService extends AbstractPaymentService {
*/
async authorizePayment(paymentSession, context = {}) {
const stat = await this.getStatus(paymentSession.data)
try {
return { data: paymentSession.data, status: stat }
} catch (error) {
@@ -242,7 +280,7 @@ class StripeProviderService extends AbstractPaymentService {
const stripeId = cart.customer?.metadata?.stripe_id || undefined
if (stripeId !== sessionData.customer) {
return this.createPayment(cart)
return await this.createPayment(cart)
} else {
if (cart.total && sessionData.amount === Math.round(cart.total)) {
return sessionData
@@ -257,6 +295,26 @@ class StripeProviderService extends AbstractPaymentService {
}
}
async updatePaymentNew(paymentSessionData, paymentInput) {
try {
const stripeId = paymentInput.customer?.metadata?.stripe_id
if (stripeId !== paymentInput.customer_id) {
return await this.createPaymentNew(paymentInput)
} else {
if (paymentSessionData.amount === Math.round(paymentInput.amount)) {
return sessionData
}
return this.stripe_.paymentIntents.update(paymentSessionData.id, {
amount: Math.round(paymentInput.amount),
})
}
} catch (error) {
throw error
}
}
async deletePayment(payment) {
try {
const { id } = payment.data
@@ -1,7 +1,6 @@
import Stripe from "stripe"
import { AbstractPaymentService, PaymentSessionStatus } from "@medusajs/medusa"
import StripeBase from "../helpers/stripe-base"
class Przelewy24ProviderService extends AbstractPaymentService {
class Przelewy24ProviderService extends StripeBase {
static identifier = "stripe-przelewy24"
constructor(
@@ -22,239 +21,9 @@ class Przelewy24ProviderService extends AbstractPaymentService {
regionService,
manager,
},
options
options,
["p24"]
)
/**
* Required Stripe options:
* {
* api_key: "stripe_secret_key", REQUIRED
* webhook_secret: "stripe_webhook_secret", REQUIRED
* // Use this flag to capture payment immediately (default is false)
* capture: true
* }
*/
this.options_ = options
/** @private @const {Stripe} */
this.stripe_ = Stripe(options.api_key)
/** @private @const {CustomerService} */
this.stripeProviderService_ = stripeProviderService
/** @private @const {CustomerService} */
this.customerService_ = customerService
/** @private @const {RegionService} */
this.regionService_ = regionService
/** @private @const {TotalsService} */
this.totalsService_ = totalsService
this.manager_ = manager
}
/**
* Fetches Stripe payment intent. Check its status and returns the
* corresponding Medusa status.
* @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)
}
/**
* Fetches a customers saved payment methods if registered in Stripe.
* @param {object} customer - customer to fetch saved cards for
* @return {Promise<Data[]>} saved payments methods
*/
async retrieveSavedMethods(customer) {
return Promise.resolve([])
}
/**
* Fetches a Stripe customer
* @param {string} customerId - Stripe customer id
* @return {Promise<object>} Stripe customer
*/
async retrieveCustomer(customerId) {
return await this.stripeProviderService_.retrieveCustomer(customerId)
}
/**
* Creates a Stripe customer using a Medusa customer.
* @param {object} customer - Customer data from Medusa
* @return {Promise<object>} Stripe customer
*/
async 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
*/
async createPayment(cart) {
const { customer_id, region_id, email } = cart
const region = await this.regionService_
.withTransaction(this.manager_)
.retrieve(region_id)
const { currency_code } = region
const amount = await this.totalsService_
.withTransaction(this.manager_)
.getTotal(cart)
const intentRequest = {
amount: Math.round(amount),
currency: currency_code,
payment_method_types: ["p24"],
capture_method: "automatic",
metadata: { cart_id: `${cart.id}` },
}
if (customer_id) {
const customer = await this.customerService_
.withTransaction(this.manager_)
.retrieve(customer_id)
if (customer.metadata?.stripe_id) {
intentRequest.customer = customer.metadata.stripe_id
} else {
const stripeCustomer = await this.createCustomer({
email,
id: customer_id,
})
intentRequest.customer = stripeCustomer.id
}
} else {
const stripeCustomer = await this.createCustomer({
email,
})
intentRequest.customer = stripeCustomer.id
}
return await this.stripe_.paymentIntents.create(intentRequest)
}
/**
* Retrieves Stripe payment intent.
* @param {PaymentData} paymentData - the data of the payment to retrieve
* @return {Promise<Data>} Stripe payment intent
*/
async retrievePayment(paymentData) {
return await this.stripeProviderService_.retrievePayment(paymentData)
}
/**
* Gets a Stripe payment intent and returns it.
* @param {PaymentSession} paymentSession - the data of the payment to retrieve
* @return {Promise<PaymentData>} Stripe payment intent
*/
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 {PaymentSession} paymentSession - payment session data
* @param {object} context - properties relevant to current context
* @return {Promise<{data: PaymentSessionData; status: PaymentSessionStatus}>} result with data and status
*/
async authorizePayment(paymentSession, context = {}) {
return await this.stripeProviderService_.authorizePayment(
paymentSession,
context
)
}
async updatePaymentData(paymentSessionData, data) {
return await this.stripeProviderService_.updatePaymentData(
paymentSessionData,
data
)
}
/**
* Updates Stripe payment intent.
* @param {PaymentSessionData} paymentSessionData - payment session data.
* @param {Cart} cart
* @return {Promise<PaymentSessionData>} Stripe payment intent
*/
async updatePayment(sessionData, cart) {
try {
const stripeId = cart.customer?.metadata?.stripe_id || undefined
if (stripeId !== sessionData.customer) {
return this.createPayment(cart)
} else {
if (cart.total && sessionData.amount === Math.round(cart.total)) {
return sessionData
}
return this.stripe_.paymentIntents.update(sessionData.id, {
amount: Math.round(cart.total),
})
}
} catch (error) {
throw error
}
}
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
* @return {object} Stripe payment intent
*/
async updatePaymentIntentCustomer(paymentIntentId, customerId) {
return await this.stripeProviderService_.updatePaymentIntentCustomer(
paymentIntentId,
customerId
)
}
/**
* Captures payment for 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 {Payment} payment - payment method data from cart
* @param {number} refundAmount - amount to refund
* @return {Promise<PaymentData>} refunded payment intent
*/
async refundPayment(payment, refundAmount) {
return await this.stripeProviderService_.refundPayment(
payment,
refundAmount
)
}
/**
* Cancels payment for Stripe 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)
}
}