Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PICT-207: Cancel Refund via Refunds API #11

Merged
merged 12 commits into from
Jul 23, 2024
23 changes: 11 additions & 12 deletions docs/CreatePayment.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ This calls Mollie's [create payment](https://docs.mollie.com/reference/create-pa

## Conditions

Adding a new transaction to an existing order with status `Initial` triggers create order payment. The transaction type should align with the payment method.
Adding a new transaction to an existing order with type Charge and state Initial triggers create order payment.

There should be only one transaction with type Charge and state Initial in your CommerceTools Payment object.

And also, the targeted Payment object should not have any transactions with type Charge and state is Pending or Success.


<br />
Expand All @@ -28,10 +32,10 @@ Adding a new transaction to an existing order with status `Initial` triggers cre
| `amountPlanned.currencyCode: "EUR"` | `amount.currency: EUR` | YES |
| `amountPlanned.centAmount: "1000"` and `amountPlanned.fractionDigits: "2"` | `amount.value: "10.00"` | YES |
| `custom.fields.sctm_create_payment_request.redirectUrl: "https://webshop.example.org/order/12345/"` | `redirectUrl: "https://webshop.example.org/order/12345/"` | YES |
| `custom.fields.sctm_create_payment_request.webhookUrl: "https://webshop.example.org/payments/webhook/"` | `redirectUrl: "https://webshop.example.org/payments/webhook/"` | NO |
| `custom.fields.sctm_create_payment_request.description: "Testing creating Mollie Payment"` | `description: "Testing creating Mollie Payment"` | YES |

The others params which listed [here](https://docs.mollie.com/reference/create-payment) can be passed through the custom field of the Payment object name **sctm_create_payment_request** with exactly
the same format like those 2 fields ``redirectUrl`` and ``webhookUrl`` above
the same format like the field ``redirectUrl`` above

<br />

Expand Down Expand Up @@ -84,14 +88,9 @@ When create order payment is successfully added on Mollie, we update CommerceToo

| Action name (CT) | Value |
| -------------------------------- | -------------------------------------------------------------------------- |
| `changeTransactionState` | `createOrderPaymentResponse: <transactionId>, state: 'Pending'` |
| `changeTransactionTimestamp` | `createOrderPaymentResponse: <createdAt>` |
| `changeTransactionInteractionId` | `transactionId: <initial CT transaction ID>` * |
| | `interactionId: <mollie payment ID>` |
| `addInterfaceInteraction` | `actionType: "CreateOrderPayment"` |
| | `id: <UUID>` |
| | `timestamp: <createdAt>` |
| | `requestValue: {<transactionId, paymentMethod>` |
| | `responseValue: <molliePaymentId, checkoutUrl, transactionId>` |
| `changeTransactionState` | `transactionId: <initialChargeTransactionId>, state: 'Pending'` |
| `changeTransactionTimestamp` | `transactionId: <initialChargeTransactionId>, timestamp: <createdAt>` |
| `changeTransactionInteractionId` | `transactionId: <initialChargeTransactionId>, interactionId: <molliePaymentId>` |
| `addInterfaceInteraction` | `actionType: "CreatePayment", id: <UUID>, timestamp: <createdAt>, requestValue: {<transactionId, paymentMethod>, responseValue: <molliePaymentId, checkoutUrl, transactionId>` |

\* Actions will always use first `Initial` transaction. There should only be one per payment. Transaction id will be the ID of the transaction which triggered the create payment.
22 changes: 9 additions & 13 deletions docs/CreatePaymentWithCreditCard.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

## Overview

To leverage [Mollie Components](https://docs.mollie.com/docs/mollie-components#add-a-submit-event-listener-to-your-form) on the Front-end side, you must send `cardToken` which was generated by Mollie inside CommerceTools's Payment object. The final result will be create a Mollie Payment, which we did on [CreatePayment.md](./CreatePayment.md)
To leverage [Mollie Components](https://docs.mollie.com/docs/mollie-components#add-a-submit-event-listener-to-your-form) on the Front-end side, you must send `cardToken` which was generated by Mollie inside CommerceTools's Payment object.

The final result will be create a Mollie Payment, which we did on [CreatePayment.md](./CreatePayment.md)

<br />

Expand All @@ -24,14 +26,13 @@ Refer to [CreatePayment.md#conditions](./CreatePayment.md#conditions)
| CT `Payment` object | Parameter (Mollie Payment) | Required |
|-----------------------------------------------------------------------------------------------------------|----------------------------------------------|----------|
| `paymentMethodInfo.method: "creditcard"` | `method: creditcard` | YES |
| `custom.fields.sctm_create_payment_request.cardToken: "card_token_12345"` | | YES |
| `custom.fields.sctm_create_payment_request.cardToken: "card_token_12345"` | | NO |
| `amountPlanned.currencyCode: "EUR"` | `amount.currency: EUR` | YES |
| `amountPlanned.centAmount: "1000"` and `amountPlanned.fractionDigits: "2"` | `amount.value: "10.00"` | YES |
| `custom.fields.sctm_create_payment_request.redirectUrl: "https://webshop.example.org/order/12345/"` | `redirectUrl: "https://webshop.example.org/order/12345/"` | YES |
| `custom.fields.sctm_create_payment_request.webhookUrl: "https://webshop.example.org/payments/webhook/"` | `redirectUrl: "https://webshop.example.org/payments/webhook/"` | NO |

The others params which listed [here](https://docs.mollie.com/reference/create-payment) can be passed through the custom field of the Payment object name **sctm_create_payment_request** with exactly
the same format like those 2 fields ``redirectUrl`` and ``webhookUrl`` above
the same format like the field ``cardToken`` above

<br />

Expand Down Expand Up @@ -84,14 +85,9 @@ When create order payment is successfully added on Mollie, we update CommerceToo

| Action name (CT) | Value |
| -------------------------------- | -------------------------------------------------------------------------- |
| `changeTransactionState` | `createOrderPaymentResponse: <transactionId>, state: 'Pending'` |
| `changeTransactionTimestamp` | `createOrderPaymentResponse: <createdAt>` |
| `changeTransactionInteractionId` | `transactionId: <initial CT transaction ID>` * |
| | `interactionId: <mollie payment ID>` |
| `addInterfaceInteraction` | `actionType: "CreateOrderPayment"` |
| | `id: <UUID>` |
| | `timestamp: <createdAt>` |
| | `requestValue: {<transactionId, paymentMethod>` |
| | `responseValue: <molliePaymentId, checkoutUrl, transactionId>` |
| `changeTransactionState` | `transactionId: <initialChargeTransactionId>, state: 'Pending'` |
| `changeTransactionTimestamp` | `transactionId: <initialChargeTransactionId>, timestamp: <createdAt>` |
| `changeTransactionInteractionId` | `transactionId: <initialChargeTransactionId>, interactionId: <molliePaymentId>` |
| `addInterfaceInteraction` | `actionType: "CreatePayment", id: <UUID>, timestamp: <createdAt>, requestValue: {<transactionId, paymentMethod>, responseValue: <molliePaymentId, checkoutUrl, transactionId>` |

\* Actions will always use first `Initial` transaction. There should only be one per payment. Transaction id will be the ID of the transaction which triggered the create payment.
98 changes: 98 additions & 0 deletions docs/CreatePaymentWithSpecificParams.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Create Payment

* [Overview](#overview)
* [Parameters map](#parameters-map)
* [Representation: CommerceTools Payment](#representation-ct-payment)
* [Creating commercetools actions from Mollie's response](#creating-commercetools-actions-from-mollies-response)

## Overview

Just like [Creating Payment with Credit Card](./CreatePaymentWithCreditCard.md), where the `cardToken` there is a specific params for the method: `creditcard` on Mollie when you are leveraging the Mollie Component to speed up the implementation on your application's Front-end side, Mollie also require some more specific params for the others method which has been pointed out on this [docs](https://docs.mollie.com/reference/extra-payment-parameters)

There are some of them are mandatory when creating payment with that specific methods, such as: applepay, paypal, giftcard, creditcard (when using Mollie Component)
The others can be filled in when the user being redirected to the Checkout URL.

<br />

**Conditions**

Refer to [CreatePayment.md#conditions](./CreatePayment.md#conditions)

<br />

## Parameters map (e.g, with method: `creditcard`)

| CT `Payment` object | Parameter (Mollie Payment) | Required |
|-----------------------------------------------------------------------------------------------------------|----------------------------------------------|----------|
| `paymentMethodInfo.method: "creditcard"` | `method: creditcard` | YES |
| `custom.fields.sctm_create_payment_request.cardToken: "card_token_12345"` | | YES |
| `amountPlanned.currencyCode: "EUR"` | `amount.currency: EUR` | YES |
| `amountPlanned.centAmount: "1000"` and `amountPlanned.fractionDigits: "2"` | `amount.value: "10.00"` | YES |
| `custom.fields.sctm_create_payment_request.redirectUrl: "https://webshop.example.org/order/12345/"` | `redirectUrl: "https://webshop.example.org/order/12345/"` | YES |
| `custom.fields.sctm_create_payment_request.webhookUrl: "https://webshop.example.org/payments/webhook/"` | `redirectUrl: "https://webshop.example.org/payments/webhook/"` | NO |
NghiaDTr marked this conversation as resolved.
Show resolved Hide resolved

The others params which listed [here](https://docs.mollie.com/reference/create-payment) can be passed through the custom field of the Payment object name **sctm_create_payment_request** with exactly
the same format like those 2 fields ``redirectUrl`` and ``webhookUrl`` above

<br />

## Representation: CommerceTools Payment

<details>
<summary>Example CommerceTools Payment object triggering creating a Mollie Payment</summary>

```json
{
"key" : "000047",
"amountPlanned" : {
"currencyCode" : "EUR",
"centAmount" : 1000,
"fractionDigits": 2
},
"paymentMethodInfo" : {
"paymentInterface" : "Mollie",
"method" : "creditcard",
"name" : {
"en" : "Credit Card"
}
},
"transactions" : [ {
"timestamp" : "2015-10-20T08:54:24.000Z",
"type" : "Charge",
"amount" : {
"currencyCode" : "USD",
"centAmount" : 1000
},
"state" : "Initial"
} ],
"custom": {
"type": {
"typeId": "type",
"key": "sctm-payment-custom-fields"
},
"fields": {
"sctm_create_payment_request": "{\"description\":\"Test\",\"locale\":\"en_GB\",\"redirectUrl\":\"https://www.google.com/\",\"cardToken\":\"card_token_12345\"}"
}
}
}
```
</details>
<br />

## Creating CommerceTools actions from Mollie's response

When create order payment is successfully added on Mollie, we update CommerceTools payment with following actions

| Action name (CT) | Value |
| -------------------------------- | -------------------------------------------------------------------------- |
| `changeTransactionState` | `createOrderPaymentResponse: <transactionId>, state: 'Pending'` |
| `changeTransactionTimestamp` | `createOrderPaymentResponse: <createdAt>` |
| `changeTransactionInteractionId` | `transactionId: <initial CT transaction ID>` * |
| | `interactionId: <mollie payment ID>` |
| `addInterfaceInteraction` | `actionType: "CreateOrderPayment"` |
| | `id: <UUID>` |
| | `timestamp: <createdAt>` |
| | `requestValue: {<transactionId, paymentMethod>` |
| | `responseValue: <molliePaymentId, checkoutUrl, transactionId>` |

\* Actions will always use first `Initial` transaction. There should only be one per payment. Transaction id will be the ID of the transaction which triggered the create payment.
9 changes: 9 additions & 0 deletions processor/src/commercetools/action.commercetools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,12 @@ export const changeTransactionState = (id: string, newState: CTTransactionState)
state: newState,
};
};

export const setTransactionCustomField = (transactionId: string, name: string, value: string) => {
return {
action: 'setTransactionCustomField',
transactionId: transactionId,
name: name,
value: value,
};
};
71 changes: 71 additions & 0 deletions processor/src/commercetools/customFields.commercetools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,74 @@ export async function createCustomPaymentInterfaceInteractionType(): Promise<voi
})
.execute();
}

export async function createCustomPaymentTransactionCancelRefundType(): Promise<void> {
const apiRoot = createApiRoot();

const customFieldName = CustomFields.paymentCancelRefund;

const {
body: { results: types },
} = await createApiRoot()
.types()
.get({
queryArgs: {
where: `key = "${customFieldName}"`,
},
})
.execute();

if (types.length > 0) {
const type = types[0];

await apiRoot
.types()
.withKey({ key: customFieldName })
.delete({
queryArgs: {
version: type.version,
},
})
.execute();
}

await apiRoot
.types()
.post({
body: {
key: customFieldName,
name: {
en: 'SCTM - Payment Cancel Refund on Transaction custom fields',
de: 'SCTM - Zahlung stornieren Rückerstattung bei benutzerdefinierten Transaktionsfeldern',
},
resourceTypeIds: ['transaction'],
fieldDefinitions: [
{
name: 'reasonText',
label: {
en: 'The reason of cancelling the refund, include the user name',
de: 'Der Grund für die Stornierung der Rückerstattung, den Benutzernamen einschließen',
},
required: false,
type: {
name: 'String',
},
inputHint: 'MultiLine',
},
{
name: 'statusText',
label: {
en: 'To differentiate between the “failure” from CommerceTools and the real status',
de: 'Um zwischen dem „Fehler“ von CommerceTools und dem tatsächlichen Status zu unterscheiden',
},
required: false,
type: {
name: 'String',
},
inputHint: 'MultiLine',
},
],
},
})
.execute();
}
1 change: 1 addition & 0 deletions processor/src/commercetools/extensions.commercetools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export async function createPaymentExtension(applicationUrl: string): Promise<vo
actions: ['Create', 'Update'],
},
],
timeoutInMs: 10000,
},
})
.execute();
Expand Down
2 changes: 2 additions & 0 deletions processor/src/connector/post-deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { assertError, assertString } from '../utils/assert.utils';
import { createPaymentExtension } from '../commercetools/extensions.commercetools';
import {
createCustomPaymentInterfaceInteractionType,
createCustomPaymentTransactionCancelRefundType,
createCustomPaymentType,
} from '../commercetools/customFields.commercetools';

Expand All @@ -18,6 +19,7 @@ async function postDeploy(properties: Map<string, unknown>): Promise<void> {
await createPaymentExtension(applicationUrl);
await createCustomPaymentType();
await createCustomPaymentInterfaceInteractionType();
await createCustomPaymentTransactionCancelRefundType();
}

async function run(): Promise<void> {
Expand Down
8 changes: 7 additions & 1 deletion processor/src/controllers/payment.controller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { determinePaymentAction } from '../utils/paymentAction.utils';
import { ControllerResponseType } from '../types/controller.types';
import { handleCreatePayment, handleListPaymentMethodsByPayment } from '../service/payment.service';
import {
handleCreatePayment,
handleListPaymentMethodsByPayment,
handlePaymentCancelRefund,
} from '../service/payment.service';
import { PaymentReference, Payment } from '@commercetools/platform-sdk';
import { ConnectorActions } from '../utils/constant.utils';
import { validateCommerceToolsPaymentPayload } from '../validators/payment.validators';
Expand Down Expand Up @@ -37,6 +41,8 @@ export const paymentController = async (
return await handleListPaymentMethodsByPayment(ctPayment);
case ConnectorActions.CreatePayment:
return await handleCreatePayment(ctPayment);
case ConnectorActions.CancelRefund:
return await handlePaymentCancelRefund(ctPayment);
default:
if (controllerAction.errorMessage === '') {
throw new SkipError('SCTM - No payment actions matched');
Expand Down
5 changes: 2 additions & 3 deletions processor/src/controllers/processor.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { apiSuccess } from '../api/success.api';
import { ControllerResponseType } from '../types/controller.types';
import { ExtensionInput } from '@commercetools/platform-sdk/dist/declarations/src/generated/models/extension';
import { logger } from '../utils/logger.utils';
import { NextFunction, Request, Response } from 'express';
import { Request, Response } from 'express';
import { paymentController } from './payment.controller';
import CustomError from '../errors/custom.error';
import SkipError from '../errors/skip.error';
Expand All @@ -16,10 +16,9 @@ import { formatErrorResponse } from '../errors/mollie.error';
*
* @param {Request} request The express request
* @param {Response} response The express response
* @param {NextFunction} next
* @returns
*/
export const post = async (request: Request, response: Response, next: NextFunction) => {
export const post = async (request: Request, response: Response) => {
try {
const { action, resource }: ExtensionInput = request.body;

Expand Down
Loading