Skip to content

Commit

Permalink
feat(payments-plugin): Setup of preventing duplicate payments
Browse files Browse the repository at this point in the history
  • Loading branch information
martijnvdbrug committed Feb 7, 2024
1 parent 7e22bce commit 0cb2df8
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 7 deletions.
14 changes: 11 additions & 3 deletions packages/payments-plugin/src/mollie/mollie.plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,9 @@ export interface MolliePluginOptions {
* MolliePlugin.init({ vendureHost: 'https://yourhost.io/', useDynamicRedirectUrl: true }),
* ]
* ```
* 2. Create a new PaymentMethod in the Admin UI, and select "Mollie payments" as the handler.
* 3. Set your Mollie apiKey in the `API Key` field.
* 2. Run a database migration to add the `mollieOrderId` custom field to the order entity.
* 3. Create a new PaymentMethod in the Admin UI, and select "Mollie payments" as the handler.
* 4. Set your Mollie apiKey in the `API Key` field.
*
* ## Specifying the redirectUrl
*
Expand All @@ -128,7 +129,6 @@ export interface MolliePluginOptions {
* By default, this option is set to `false` for backwards compatibility. In a future version, this option will be deprecated.
* Upon deprecation, the `redirectUrl` will always be passed as an argument to the `createPaymentIntent` mutation.
*
* TODO toevoegen van /code weggehaald..!
* ## Storefront usage
*
* In your storefront you add a payment to an order using the `createMolliePaymentIntent` mutation. In this example, our Mollie
Expand Down Expand Up @@ -196,6 +196,14 @@ export interface MolliePluginOptions {
* If you don't want this behaviour (Authorized first), you can set 'autoCapture=true' on the payment method. This option will immediately
* capture the payment after a customer authorizes the payment.
*
* ## ArrangingAdditionalPayment state
*
* In some rare cases, a customer can add items to the active order, while a Mollie payment is still open,
* for example by opening your storefront in another browser tab.
* This could result in an order being in `ArrangingAdditionalPayment` status after the customer finished payment.
* You should check if there is still an active order with status `ArrangingAdditionalPayment` on your order confirmation page,
* and if so, allow your customer to pay for the additional items.
*
* @docsCategory core plugins/PaymentsPlugin
* @docsPage MolliePlugin
* @docsWeight 0
Expand Down
24 changes: 20 additions & 4 deletions packages/payments-plugin/src/mollie/mollie.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
ProductVariantService,
RequestContext,
} from '@vendure/core';
import { OrderStateMachine } from '@vendure/core/dist/service/helpers/order-state-machine/order-state-machine';
import { totalCoveredByPayments } from '@vendure/core/dist/service/helpers/utils/order-utils';

import { loggerCtx, PLUGIN_INIT_OPTIONS } from './constants';
Expand Down Expand Up @@ -55,6 +56,8 @@ class InvalidInputError implements MolliePaymentIntentError {

@Injectable()
export class MollieService {
private readonly injector: Injector;

constructor(
private paymentMethodService: PaymentMethodService,
@Inject(PLUGIN_INIT_OPTIONS) private options: MolliePluginOptions,
Expand All @@ -63,7 +66,9 @@ export class MollieService {
private entityHydrator: EntityHydrator,
private variantService: ProductVariantService,
private moduleRef: ModuleRef,
) {}
) {
this.injector = new Injector(this.moduleRef);
}

/**
* Creates a redirectUrl to Mollie for the given paymentMethod and current activeOrder
Expand Down Expand Up @@ -139,6 +144,10 @@ export class MollieService {
}
redirectUrl = paymentMethodRedirectUrl;
}
if (order.state !== 'ArrangingPayment' && order.state !== 'ArrangingAdditionalPayment') {
// TODO get order state machine and check if transitionable to ArrangingPayment
// const orderStateMachine = this.injector.get(OrderStateMachine);
}
const variantsWithInsufficientSaleableStock = await this.getVariantsWithInsufficientStock(ctx, order);
if (variantsWithInsufficientSaleableStock.length) {
return new PaymentIntentError(
Expand Down Expand Up @@ -231,7 +240,14 @@ export class MollieService {
`Unable to find order ${mollieOrder.orderNumber}, unable to process Mollie order ${mollieOrder.id}`,
);
}
if (order.state === 'PaymentSettled' || order.state === 'Shipped' || order.state === 'Delivered') {
if (
order.state === 'PaymentSettled' ||
order.state === 'Cancelled' ||
order.state === 'Shipped' ||
order.state === 'PartiallyShipped' ||
order.state === 'Delivered' ||
order.state === 'PartiallyDelivered'
) {
Logger.info(
`Order ${order.code} is already '${order.state}', no need for handling Mollie status '${mollieOrder.status}'`,
loggerCtx,
Expand Down Expand Up @@ -280,7 +296,7 @@ export class MollieService {
paymentMethodCode: string,
status: 'Authorized' | 'Settled',
): Promise<Order> {
if (order.state !== 'ArrangingPayment') {
if (order.state !== 'ArrangingPayment' && order.state !== 'ArrangingAdditionalPayment') {
const transitionToStateResult = await this.orderService.transitionToState(
ctx,
order.id,
Expand Down Expand Up @@ -347,7 +363,7 @@ export class MollieService {
const client = createMollieClient({ apiKey });
const activeOrder = await this.activeOrderService.getActiveOrder(ctx, undefined);
const additionalParams = await this.options.enabledPaymentMethodsParams?.(
new Injector(this.moduleRef),
this.injector,
ctx,
activeOrder ?? null,
);
Expand Down

0 comments on commit 0cb2df8

Please sign in to comment.