Skip to content

Commit

Permalink
Merge pull request #135 from mollie/feature/MOL-78/PICT-204
Browse files Browse the repository at this point in the history
Feature/mol 78/pict 204
  • Loading branch information
tdang1-shopmacher authored Feb 21, 2025
2 parents b419736 + 01a49dd commit 113198c
Show file tree
Hide file tree
Showing 32 changed files with 2,012 additions and 742 deletions.
24 changes: 21 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,24 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).

## v1.2.3

Added

- Capture payment feature

## v1.2.2

Fixed

- Bugs fixing for custom application unit tests

## v1.2.1

Updated

- Move setting of credit card component visibility and bank transfer payment method due date into the custom application

## v1.2.0

Added
Expand Down Expand Up @@ -45,7 +63,7 @@ Added

- New custom field for transaction: `sctm_transaction_refund_for_mollie_payment` which would store the Mollie Payment ID that need to be refunded

Fixes
Fixed

[Create Refund](./docs/CreateRefund.md)
- Handling the Refund Creation for the case that the Payment has more than one Success Charge transaction
Expand Down Expand Up @@ -251,7 +269,7 @@ Added

## v1.1.1

Fixes
Fixed

- Type converting issue in payment method listing endpoint

Expand All @@ -278,7 +296,7 @@ Added

## v1.0.2

Fixes
Fixed

- Fix the issue that the payment method is not correctly set in some cases

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const AvailabilityDetailsForm = (props: TAvailabilityDetailsFormProps) => {
const [selectedCurrency, setSelectedCurrency] = useState<TCurrencyCode>(
projectCurrencies[0] as TCurrencyCode
);

const [countryOptions, setCountryOptions] = useState<
{ value: string; label: string }[]
>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ const AvailabilityDetails = (props: TAvailabilityDetailFormProps) => {
maxAmount: maxAmount?.toString(),
surchargeCost: {
percentageAmount: surchargeCost?.percentageAmount ?? 0,
fixedAmount: surchargeCost?.fixedAmount.toString() ?? '0',
fixedAmount: surchargeCost?.fixedAmount?.toString() ?? '0',
},
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,62 @@
import {
screen,
render,
screen,
} from '@commercetools-frontend/application-shell/test-utils';
import { Suspense } from 'react';
import { MemoryRouter } from 'react-router';
import { IntlProvider } from 'react-intl';
import AvailabilityList from './list';
import { TFetchCustomObjectDetailsQuery } from '../../../types/generated/ctp';
import { useApplicationContext } from '@commercetools-frontend/application-shell-connectors';
import { useMcMutation } from '@commercetools-frontend/application-shell';
import { useShowNotification } from '@commercetools-frontend/actions-global';

jest.mock('@commercetools-frontend/application-shell-connectors', () => ({
useApplicationContext: jest.fn(),
}));
jest.mock('@commercetools-frontend/application-shell', () => ({
useMcMutation: jest.fn(),
}));
jest.mock('@commercetools-frontend/actions-global', () => ({
useShowNotification: jest.fn(),
}));

describe('test MethodDetails.tsx', () => {
it('test render', async () => {
const pricingConstraints = [
{
id: 1,
currency: 'GBP1',
country: 'UK1',
currencyCode: 'GBP1',
countryCode: 'UK1',
minAmount: 100,
maxAmount: 2000,
surchargeCost: '2%',
surchargeCost: {
percentageAmount: 2,
renderedText: '2%',
},
},
{
id: 2,
currency: 'GBP2',
country: 'DE2',
currencyCode: 'GBP2',
countryCode: 'DE2',
minAmount: 1000,
maxAmount: 30000,
surchargeCost: '4%',
surchargeCost: {
percentageAmount: 4,
renderedText: '4%',
},
},
{
id: 3,
currency: 'EUR3',
country: 'DE3',
currencyCode: 'EUR3',
countryCode: 'DE3',
minAmount: 500,
maxAmount: 10000,
surchargeCost: '2 % + € 0,35',
surchargeCost: {
percentageAmount: 2,
fixedAmount: 100,
renderedText: '2% + 100EUR3',
},
},
];

Expand Down Expand Up @@ -62,7 +85,23 @@ describe('test MethodDetails.tsx', () => {
} as unknown as string,
};

render(
(useApplicationContext as jest.Mock).mockReturnValue({
dataLocale: 'de',
projectLanguages: ['de'],
projectCurrencies: ['GBP1', 'GBP2', 'EUR3'],
projectCountries: ['UK1', 'DE2', 'DE3'],
});

(useMcMutation as jest.Mock).mockReturnValue([
jest.fn().mockResolvedValue({}),
{
loading: false,
},
]);

(useShowNotification as jest.Mock).mockReturnValue(jest.fn());

const result = render(
<MemoryRouter>
<Suspense fallback={<div>Loading...</div>}>
<IntlProvider
Expand All @@ -76,12 +115,12 @@ describe('test MethodDetails.tsx', () => {
);

pricingConstraints.forEach(
({ currency, country, minAmount, maxAmount, surchargeCost }) => {
expect(screen.getByText(currency)).not.toBeNull();
expect(screen.getByText(country)).not.toBeNull();
({ currencyCode, countryCode, minAmount, maxAmount, surchargeCost }) => {
expect(screen.getByText(currencyCode)).not.toBeNull();
expect(screen.getByText(countryCode)).not.toBeNull();
expect(screen.getByText(minAmount)).not.toBeNull();
expect(screen.getByText(maxAmount)).not.toBeNull();
expect(screen.getByText(surchargeCost)).not.toBeNull();
expect(screen.getByText(surchargeCost.renderedText)).not.toBeNull();
}
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ describe('Test method-details.tsx', () => {
(useApplicationContext as jest.Mock).mockReturnValue({
dataLocale: 'de',
projectLanguages: ['de'],
projectCurrencies: ['DE'],
projectCountries: ['EUR'],
});

(useShowNotification as jest.Mock).mockReturnValue(jest.fn());
Expand Down
159 changes: 159 additions & 0 deletions docs/CapturePayment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# Capture Payment

* [Overview](#overview)
* [Conditions](#conditions)
* [Transactions' custom fields](#transactions-custom-fields)
* [Example payload](#example-payload)
* [Example response](#example-response)

## Overview

This feature is to capture the money of an authorized Mollie payment and update transactions with correct status and information.

It calls Mollie [Capture API](https://docs.mollie.com/reference/captures-api) eventually.

To trigger the capture for a certain payment, simply update the target pending transaction of type `Charge` with our predefined custom type named `sctm_capture_payment_request`

| **Custom fields** | **Required** | **Description** |
| --- | --- | --- |
| `sctm_should_capture` | false | WHEN its value set to `true`, the Processor will start to capture with associated payment information |
| `sctm_capture_description` | false | Hold the description of the capture |

## Conditions

1. A valid authorized CT payment must contain the two follow transactions:
* One with type of `authorization` and state `success`
* One with type of `charge` and state `pending` | `failure` with (in case previous capture attempt failed and we want to retry) with custom field `sctm_should_capture` = `true`
2. The ref Mollie payment must have `captureMode` equal to `manual` and `state` equal to `authorized`

## Transactions' custom fields

| Fields | Data type | Required | Usage |
|-----------------------------------------------------------------------------------------------------------|----------------------------------------------|----------|------------------|
| `sctm_should_capture` | `boolean` | NO | If `true`, trigger the capture process |
| `sctm_capture_description` | `string` | NO | Contain the description for the capture |

## Example payload

```json
ENDPOINT: `https://api.europe-west1.gcp.commercetools.com/{{your_project_key}}/payments/{{payment_id_for_capture}}`
{
"version": {{capture_payment_version}},
"actions": [
{
"action" : "setTransactionCustomType",
"type" : {
"id" : "{{capture_type_id}}",
"typeId" : "type"
},
"fields" : {
"sctm_should_capture" : true,
"sctm_capture_description": "Capture description"
},
"transactionId" : "{{capture_transaction_id}}"
}
]
}
```

## Example response

```json
{
"id": "ba410236-3135-4037-b61e-10aa82a1ae99",
"version": 26,
"versionModifiedAt": "2025-02-17T08:12:45.109Z",
"lastMessageSequenceNumber": 6,
"createdAt": "2025-02-17T08:09:06.565Z",
"lastModifiedAt": "2025-02-17T08:12:45.109Z",
"lastModifiedBy": {
"clientId": "8a_rNd-HokSybRmRRqdEuh82",
"isPlatformClient": false
},
"createdBy": {
"clientId": "8a_rNd-HokSybRmRRqdEuh82",
"isPlatformClient": false
},
"amountPlanned": {
"type": "centPrecision",
"currencyCode": "EUR",
"centAmount": 11999,
"fractionDigits": 2
},
"paymentMethodInfo": {
"paymentInterface": "mollie",
"method": "creditcard",
"name": {
"en": "creditcard",
"de": "creditcard"
}
},
"custom": {
"type": {
"typeId": "type",
"id": "53cdd626-7884-4421-9226-d81ba7038424"
},
"fields": {
"sctm_payment_methods_request": "{\"locale\":\"de_DE\",\"billingCountry\":\"DE\",\"includeWallets\":\"applepay\"}",
"sctm_mollie_profile_id": "pfl_SPkYGiEQjf",
"sctm_payment_methods_response": "{\"count\":3,\"methods\":[{\"id\":\"creditcard\",\"name\":{\"en-GB\":\"Card\",\"de-DE\":\"Card\",\"en-US\":\"Card\"},\"description\":{\"en-GB\":\"\",\"de-DE\":\"\",\"en-US\":\"\"},\"image\":\"https://www.mollie.com/external/icons/payment-methods/creditcard.svg\",\"order\":20},{\"id\":\"applepay\",\"name\":{\"en-GB\":\"Apple Pay\",\"de-DE\":\"Apple Pay\",\"en-US\":\"Apple Pay\"},\"description\":{\"en-GB\":\"\",\"de-DE\":\"\",\"en-US\":\"\"},\"image\":\"https://www.mollie.com/external/icons/payment-methods/applepay.svg\",\"order\":0},{\"id\":\"banktransfer\",\"name\":{\"en-GB\":\"Bank transfer\",\"de-DE\":\"Bank transfer\",\"en-US\":\"Bank transfer\"},\"description\":{\"en-GB\":\"\",\"de-DE\":\"\",\"en-US\":\"\"},\"image\":\"https://www.mollie.com/external/icons/payment-methods/banktransfer.svg\",\"order\":0}]}",
"sctm_create_payment_request": "{\"description\":\"Testing creating Mollie payment\",\"redirectUrl\":\"http://localhost:3000/thank-you?orderId=ae22-e03f-aab1\",\"billingAddress\":{\"givenName\":\"thach\",\"familyName\":\"dang\",\"streetAndNumber\":\"Am campus 5\",\"postalCode\":\"48721\",\"city\":\"Gescher\",\"country\":\"DE\",\"phone\":\"49254287030\",\"email\":\"[email protected]\"},\"shippingAddress\":{\"givenName\":\"thach\",\"familyName\":\"dang\",\"streetAndNumber\":\"Am campus 5\",\"postalCode\":\"48721\",\"city\":\"Gescher\",\"country\":\"DE\",\"phone\":\"49254287030\",\"email\":\"[email protected]\"},\"billingEmail\":\"[email protected]\",\"cardToken\":\"tkn_h3mrzMtest\",\"lines\":[{\"description\":\"Geometrischer Kissenbezug\",\"quantity\":1,\"quantityUnit\":\"pcs\",\"unitPrice\":{\"currency\":\"EUR\",\"value\":\"19.99\"},\"totalAmount\":{\"currency\":\"EUR\",\"value\":\"19.99\"}}],\"captureMode\":\"manual\"}"
}
},
"paymentStatus": {
"interfaceText": "initial"
},
"transactions": [
{
"id": "0c80239c-ade7-4556-a871-f4317759be10",
"timestamp": "2025-02-17T08:09:36.000Z",
"type": "Charge",
"amount": {
"type": "centPrecision",
"currencyCode": "EUR",
"centAmount": 11999,
"fractionDigits": 2
},
"interactionId": "tr_mDmhmxTzkX",
"state": "Success",
"custom": {
"type": {
"typeId": "type",
"id": "418c568c-15a5-4418-9698-a81edcffc471"
},
"fields": {
"sctm_should_capture": true,
"sctm_capture_description": "Capture on 2025-02-17T08:12:42.171Z"
}
}
},
{
"id": "d39ed5fc-f8ae-405b-ae99-3f713d243da0",
"type": "Authorization",
"amount": {
"type": "centPrecision",
"currencyCode": "EUR",
"centAmount": 11999,
"fractionDigits": 2
},
"interactionId": "tr_mDmhmxTzkX",
"state": "Success"
}
],
"interfaceInteractions": [
{
"type": {
"typeId": "type",
"id": "9d9b436c-58cc-4f2e-a393-0fbd05ba0193"
},
"fields": {
"sctm_id": "6e134208-fdfe-453f-928e-8ab44d9b10ec",
"sctm_action_type": "createPayment",
"sctm_created_at": "2025-02-17T08:09:36+00:00",
"sctm_request": "{\"transactionId\":\"0c80239c-ade7-4556-a871-f4317759be10\",\"paymentMethod\":\"creditcard\"}",
"sctm_response": "{\"molliePaymentId\":\"tr_mDmhmxTzkX\",\"checkoutUrl\":\"https://www.mollie.com/checkout/test-mode?method=creditcard&token=6.8hhrnm\",\"transactionId\":\"0c80239c-ade7-4556-a871-f4317759be10\"}"
}
}
]
}
```
Loading

0 comments on commit 113198c

Please sign in to comment.