From ce5e5d04e6d2f86cb5c9363730946c02d35d49b9 Mon Sep 17 00:00:00 2001 From: Martin Schalter Date: Wed, 30 Aug 2023 18:06:07 +0200 Subject: [PATCH 1/3] add cancelUrl to order and payment --- src/binders/orders/parameters.ts | 2 +- src/binders/payments/parameters.ts | 2 +- src/data/orders/data.ts | 8 ++++++++ src/data/payments/data.ts | 8 ++++++++ tests/unit/models/order.test.ts | 1 + tests/unit/models/payment.test.ts | 1 + tests/unit/resources/customers/payments.test.ts | 3 +++ tests/unit/resources/orders.test.ts | 6 ++++++ tests/unit/resources/payments.test.ts | 5 +++++ tests/unit/resources/subscriptions/payments.test.ts | 2 ++ 10 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/binders/orders/parameters.ts b/src/binders/orders/parameters.ts index 39b59061..b83eb42e 100644 --- a/src/binders/orders/parameters.ts +++ b/src/binders/orders/parameters.ts @@ -6,7 +6,7 @@ import { type CreateParameters as PaymentCreateParameters } from '../payments/pa import type PickOptional from '../../types/PickOptional'; export type CreateParameters = Pick & - PickOptional & { + PickOptional & { /** * All order lines must have the same currency as the order. You cannot mix currencies within a single order. * diff --git a/src/binders/payments/parameters.ts b/src/binders/payments/parameters.ts index 09599c13..2637cdd0 100644 --- a/src/binders/payments/parameters.ts +++ b/src/binders/payments/parameters.ts @@ -4,7 +4,7 @@ import { type PaymentData, type PaymentEmbed, type PaymentInclude } from '../../ import { type IdempotencyParameter, type PaginationParameters, type ThrottlingParameter } from '../../types/parameters'; import type PickOptional from '../../types/PickOptional'; -export type CreateParameters = Pick & +export type CreateParameters = Pick & PickOptional & { /** * Normally, a payment method screen is shown. However, when using this parameter, you can choose a specific payment method and your customer will skip the selection screen and is sent directly to diff --git a/src/data/orders/data.ts b/src/data/orders/data.ts index b6373128..b0bf154d 100644 --- a/src/data/orders/data.ts +++ b/src/data/orders/data.ts @@ -114,6 +114,14 @@ export interface OrderData extends Model<'order'> { * @see https://docs.mollie.com/reference/v2/orders-api/get-order?path=redirectUrl#response */ redirectUrl: Nullable; + /** + * The optional redirect URL you provided during payment creation. Consumer that explicitly cancel the order will be redirected to this URL if provided, or otherwise to the `redirectUrl` instead — see above. + * + * The URL will be `null` for recurring orders. + * + * @see https://docs.mollie.com/reference/v2/orders-api/get-order?path=cancelUrl#response + */ + cancelUrl: Nullable; /** * An array of order line objects. Each object will have the properties listed below. * diff --git a/src/data/payments/data.ts b/src/data/payments/data.ts index e1564fdd..645ecf3f 100644 --- a/src/data/payments/data.ts +++ b/src/data/payments/data.ts @@ -112,6 +112,14 @@ export interface PaymentData extends Model<'payment'> { * @see https://docs.mollie.com/reference/v2/payments-api/get-payment?path=redirectUrl#response */ redirectUrl?: string; + /** + * The optional redirect URL you provided during payment creation. Consumer that explicitly cancel the payment will be redirected to this URL if provided, or otherwise to the `redirectUrl` instead — see above. + * + * The URL will be `null` for recurring payments. + * + * @see https://docs.mollie.com/reference/v2/payments-api/get-payment?path=cancelUrl#response + */ + cancelUrl?: string; /** * The URL Mollie will call as soon an important status change takes place. * diff --git a/tests/unit/models/order.test.ts b/tests/unit/models/order.test.ts index d9063a1e..579056e6 100644 --- a/tests/unit/models/order.test.ts +++ b/tests/unit/models/order.test.ts @@ -53,6 +53,7 @@ async function getOrder(status, additionalLinks?: object) { method: 'klarnapaylater', isCancelable: true, redirectUrl: 'https://example.org/redirect', + cancelUrl: 'https://example.org/cancel', webhookUrl: 'https://example.org/webhook', lines: [ { diff --git a/tests/unit/models/payment.test.ts b/tests/unit/models/payment.test.ts index dd9335ae..edc50ca2 100644 --- a/tests/unit/models/payment.test.ts +++ b/tests/unit/models/payment.test.ts @@ -37,6 +37,7 @@ async function getPayment(status, additionalProperties?: object, additionalLinks profileId: 'pfl_2A1gacu42V', sequenceType: 'oneoff', redirectUrl: 'https://example.org/redirect', + cancelUrl: 'https://example.org/cancel', webhookUrl: 'https://example.org/webhook', settlementAmount: { value: '20.00', diff --git a/tests/unit/resources/customers/payments.test.ts b/tests/unit/resources/customers/payments.test.ts index 93a60a57..003cd87b 100644 --- a/tests/unit/resources/customers/payments.test.ts +++ b/tests/unit/resources/customers/payments.test.ts @@ -24,6 +24,7 @@ test('createCustomerPayment', async () => { profileId: 'pfl_2A1gacu42V', sequenceType: 'oneoff', redirectUrl: 'https://example.org/redirect', + cancelUrl: 'https://example.org/cancel', webhookUrl: 'https://example.org/webhook', _links: { self: { @@ -53,6 +54,7 @@ test('createCustomerPayment', async () => { customerId: 'cst_FhQJRw4s2n', description: 'My first API payment', redirectUrl: 'https://example.org/redirect', + cancelUrl: 'https://example.org/cancel', webhookUrl: 'https://example.org/webhook', metadata: { order_id: '1234', @@ -75,6 +77,7 @@ test('createCustomerPayment', async () => { expect(payment.profileId).toBe('pfl_2A1gacu42V'); expect(payment.sequenceType).toBe('oneoff'); expect(payment.redirectUrl).toBe('https://example.org/redirect'); + expect(payment.cancelUrl).toBe('https://example.org/cancel'); expect(payment.webhookUrl).toBe('https://example.org/webhook'); expect(payment._links.self).toEqual({ href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', type: 'application/hal+json' }); diff --git a/tests/unit/resources/orders.test.ts b/tests/unit/resources/orders.test.ts index 1474a9e1..d152585b 100644 --- a/tests/unit/resources/orders.test.ts +++ b/tests/unit/resources/orders.test.ts @@ -50,6 +50,7 @@ function composeOrderResponse(orderId, orderStatus = 'created', orderNumber = '1 method: 'klarnapaylater', isCancelable: true, redirectUrl: 'https://example.org/redirect', + cancelUrl: 'https://example.org/cancel', webhookUrl: 'https://example.org/webhook', lines: [ { @@ -182,6 +183,7 @@ function testOrder(order, orderId, orderStatus = 'created', orderNumber = '1337' expect(order.locale).toBe('nl_NL'); expect(order.redirectUrl).toBe('https://example.org/redirect'); + expect(order.cancelUrl).toBe('https://example.org/cancel'); expect(order.webhookUrl).toBe('https://example.org/webhook'); expect(order._links.self).toEqual({ @@ -275,6 +277,7 @@ test('createOrder', async () => { locale: 'nl_NL', orderNumber: '1337', redirectUrl: 'https://example.org/redirect', + cancelUrl: 'https://example.org/cancel', webhookUrl: 'https://example.org/webhook', method: 'klarnapaylater', lines: [ @@ -380,6 +383,7 @@ test('getOrderIncludingPayments', async () => { email: 'luke@skywalker.com', }, redirectUrl: 'https://example.org/redirect', + cancelUrl: 'https://example.org/cancel', lines: [ { resource: 'orderline', @@ -515,6 +519,7 @@ test('getOrderIncludingPayments', async () => { orderId: 'ord_kEn1PlbGa', sequenceType: 'oneoff', redirectUrl: 'https://example.org/redirect', + cancelUrl: 'https://example.org/cancel', _links: { self: { href: 'https://api.mollie.com/v2/payments/tr_ncaPcAhuUV', @@ -568,6 +573,7 @@ test('getOrderIncludingPayments', async () => { expect(payment.orderId).toBe('ord_kEn1PlbGa'); expect(payment.sequenceType).toBe('oneoff'); expect(payment.redirectUrl).toBe('https://example.org/redirect'); + expect(payment.cancelUrl).toBe('https://example.org/cancel'); expect(payment._links.self).toEqual({ href: 'https://api.mollie.com/v2/payments/tr_ncaPcAhuUV', type: 'application/hal+json', diff --git a/tests/unit/resources/payments.test.ts b/tests/unit/resources/payments.test.ts index 09e395fc..ee0ef5a3 100644 --- a/tests/unit/resources/payments.test.ts +++ b/tests/unit/resources/payments.test.ts @@ -24,6 +24,7 @@ test('createPayment', async () => { profileId: 'pfl_2A1gacu42V', sequenceType: 'oneoff', redirectUrl: 'https://example.org/redirect', + cancelUrl: 'https://example.org/cancel', webhookUrl: 'https://example.org/webhook', _links: { self: { @@ -48,6 +49,7 @@ test('createPayment', async () => { }, description: 'My first API payment', redirectUrl: 'https://example.org/redirect', + cancelUrl: 'https://example.org/cancel', webhookUrl: 'https://example.org/webhook', metadata: { order_id: '1234', @@ -70,6 +72,7 @@ test('createPayment', async () => { expect(payment.profileId).toBe('pfl_2A1gacu42V'); expect(payment.sequenceType).toBe('oneoff'); expect(payment.redirectUrl).toBe('https://example.org/redirect'); + expect(payment.cancelUrl).toBe('https://example.org/cancel'); expect(payment.webhookUrl).toBe('https://example.org/webhook'); expect(payment._links.self).toEqual({ href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', type: 'application/hal+json' }); @@ -190,6 +193,7 @@ test('getPayment', async () => { profileId: 'pfl_2A1gacu42V', sequenceType: 'oneoff', redirectUrl: 'https://example.org/redirect', + cancelUrl: 'https://example.org/cancel', webhookUrl: 'https://example.org/webhook', settlementAmount: { value: '20.00', @@ -226,6 +230,7 @@ test('getPayment', async () => { expect(payment.profileId).toBe('pfl_2A1gacu42V'); expect(payment.sequenceType).toBe('oneoff'); expect(payment.redirectUrl).toBe('https://example.org/redirect'); + expect(payment.cancelUrl).toBe('https://example.org/cancel'); expect(payment.webhookUrl).toBe('https://example.org/webhook'); expect(payment._links.self).toEqual({ href: 'https://api.mollie.com/v2/payments/tr_44aKxzEbr8', type: 'application/hal+json' }); diff --git a/tests/unit/resources/subscriptions/payments.test.ts b/tests/unit/resources/subscriptions/payments.test.ts index bc792c09..231106ba 100644 --- a/tests/unit/resources/subscriptions/payments.test.ts +++ b/tests/unit/resources/subscriptions/payments.test.ts @@ -28,6 +28,7 @@ test('listSubscriptionPayments', async () => { subscriptionId: 'sub_8JfGzs6v3K', sequenceType: 'recurring', redirectUrl: null, + cancelUrl: null, webhookUrl: 'https://example.org/webhook', settlementAmount: { value: '10.00', @@ -84,6 +85,7 @@ test('listSubscriptionPayments', async () => { subscriptionId: 'sub_8JfGzs6v3K', sequenceType: 'recurring', redirectUrl: null, + cancelUrl: null, webhookUrl: 'https://example.org/webhook', settlementAmount: { value: '10.00', From 0883fbeb0e468c1cdac6af1a47ea92142f99a098 Mon Sep 17 00:00:00 2001 From: "Pimm \"de Chinchilla\" Hogeling" Date: Sun, 17 Sep 2023 16:19:15 +0200 Subject: [PATCH 2/3] Add cancelUrl to payment update and order update endpoints. --- src/binders/orders/parameters.ts | 2 +- src/binders/payments/parameters.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/binders/orders/parameters.ts b/src/binders/orders/parameters.ts index b83eb42e..fb98229c 100644 --- a/src/binders/orders/parameters.ts +++ b/src/binders/orders/parameters.ts @@ -89,7 +89,7 @@ export interface GetParameters { embed?: OrderEmbed[]; } -export type UpdateParameters = PickOptional & { +export type UpdateParameters = PickOptional & { orderNumber?: string; testmode?: boolean; }; diff --git a/src/binders/payments/parameters.ts b/src/binders/payments/parameters.ts index 2637cdd0..54c4b029 100644 --- a/src/binders/payments/parameters.ts +++ b/src/binders/payments/parameters.ts @@ -174,7 +174,7 @@ export type PageParameters = PaginationParameters & { export type IterateParameters = Omit & ThrottlingParameter; -export type UpdateParameters = Pick & +export type UpdateParameters = Pick & PickOptional & { /** * For digital goods in most jurisdictions, you must apply the VAT rate from your customer's country. Choose the VAT rates you have used for the order to ensure your customer's country matches the From dbe814fc5cb21b3a925deba919db83e250d6f085 Mon Sep 17 00:00:00 2001 From: "Pimm \"de Chinchilla\" Hogeling" Date: Sun, 17 Sep 2023 16:32:00 +0200 Subject: [PATCH 3/3] Pull documentation from docs.mollie.com. --- src/data/orders/data.ts | 6 +++++- src/data/payments/data.ts | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/data/orders/data.ts b/src/data/orders/data.ts index b0bf154d..b94acf22 100644 --- a/src/data/orders/data.ts +++ b/src/data/orders/data.ts @@ -115,7 +115,11 @@ export interface OrderData extends Model<'order'> { */ redirectUrl: Nullable; /** - * The optional redirect URL you provided during payment creation. Consumer that explicitly cancel the order will be redirected to this URL if provided, or otherwise to the `redirectUrl` instead — see above. + * The optional redirect URL you provided during payment creation. Consumer that explicitly cancel the order will be redirected to this URL if provided, or otherwise to the `redirectUrl` instead — + * see above. + * + * Mollie will always give you status updates via webhooks, including for the `canceled` status. This parameter is therefore entirely optional, but can be useful when implementing a dedicated + * consumer-facing flow to handle order cancellations. * * The URL will be `null` for recurring orders. * diff --git a/src/data/payments/data.ts b/src/data/payments/data.ts index 645ecf3f..c7b5df0f 100644 --- a/src/data/payments/data.ts +++ b/src/data/payments/data.ts @@ -113,7 +113,11 @@ export interface PaymentData extends Model<'payment'> { */ redirectUrl?: string; /** - * The optional redirect URL you provided during payment creation. Consumer that explicitly cancel the payment will be redirected to this URL if provided, or otherwise to the `redirectUrl` instead — see above. + * The optional redirect URL you provided during payment creation. Consumer that explicitly cancel the payment will be redirected to this URL if provided, or otherwise to the `redirectUrl` instead — + * see above. + * + * Mollie will always give you status updates via webhooks, including for the `canceled` status. This parameter is therefore entirely optional, but can be useful when implementing a dedicated + * consumer-facing flow to handle payment cancellations. * * The URL will be `null` for recurring payments. *