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

Add captures API endpoints #371

Merged
merged 12 commits into from
Sep 30, 2024
28 changes: 21 additions & 7 deletions src/binders/payments/captures/PaymentCapturesBinder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import checkId from '../../../plumbing/checkId';
import renege from '../../../plumbing/renege';
import type Callback from '../../../types/Callback';
import Binder from '../../Binder';
import { type GetParameters, type IterateParameters, type PageParameters } from './parameters';
import { type CreateParameters, type GetParameters, type IterateParameters, type PageParameters } from './parameters';

function getPathSegments(paymentId: string) {
return `payments/${paymentId}/captures`;
Expand All @@ -20,11 +20,27 @@ export default class PaymentCapturesBinder extends Binder<CaptureData, Capture>
alias(this, { page: ['all', 'list'] });
}

/**
* Capture an *authorized* payment.
*
* @since 4.1.0
* @see https://docs.mollie.com/reference/create-capture
*/
public create(parameters: CreateParameters): Promise<Capture>;
public create(parameters: CreateParameters, callback: Callback<Capture>): void;
public create(parameters: CreateParameters) {
if (renege(this, this.create, ...arguments)) return;
const { paymentId, ...data } = parameters;
if (!checkId(paymentId, 'payment')) {
throw new ApiError('The payment id is invalid');
}
return this.networkClient.post<CaptureData, Capture>(getPathSegments(paymentId), data);
}

/**
* Retrieve a single capture by its ID. Note the original payment's ID is needed as well.
*
* Captures are used for payments that have the *authorize-then-capture* flow. The only payment methods at the moment that have this flow are **Klarna Pay now**, **Klarna Pay later** and **Klarna
* Slice it**.
* Captures are used for payments that have the *authorize-then-capture* flow. Mollie currently supports captures for **Cards** and **Klarna**.
*
* @since 1.1.1
* @see https://docs.mollie.com/reference/v2/captures-api/get-capture
Expand All @@ -46,8 +62,7 @@ export default class PaymentCapturesBinder extends Binder<CaptureData, Capture>
/**
* Retrieve all captures for a certain payment.
*
* Captures are used for payments that have the *authorize-then-capture* flow. The only payment methods at the moment that have this flow are *Klarna Pay now*, *Klarna Pay later* and *Klarna Slice
* it*.
* Captures are used for payments that have the *authorize-then-capture* flow. Mollie currently supports captures for **Cards** and **Klarna**.
*
* @since 3.0.0
* @see https://docs.mollie.com/reference/v2/captures-api/list-captures
Expand All @@ -66,8 +81,7 @@ export default class PaymentCapturesBinder extends Binder<CaptureData, Capture>
/**
* Retrieve all captures for a certain payment.
*
* Captures are used for payments that have the *authorize-then-capture* flow. The only payment methods at the moment that have this flow are *Klarna Pay now*, *Klarna Pay later* and *Klarna Slice
* it*.
* Captures are used for payments that have the *authorize-then-capture* flow. Mollie currently supports captures for **Cards** and **Klarna**.
*
* @since 3.6.0
* @see https://docs.mollie.com/reference/v2/captures-api/list-captures
Expand Down
14 changes: 9 additions & 5 deletions src/binders/payments/captures/parameters.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import { type CaptureEmbed } from '../../../data/payments/captures/data';
import { type PaginationParameters, type ThrottlingParameter } from '../../../types/parameters';
import { type CaptureData, type CaptureInclude } from '../../../data/payments/captures/data';
import { type IdempotencyParameter, type PaginationParameters, type ThrottlingParameter } from '../../../types/parameters';
import type PickOptional from '../../../types/PickOptional';

interface ContextParameters {
paymentId: string;
testmode?: boolean;
}

export type CreateParameters = ContextParameters & PickOptional<CaptureData, 'amount' | 'description' | 'metadata'> & IdempotencyParameter;

export type GetParameters = ContextParameters & {
embed?: CaptureEmbed[];
include?: CaptureInclude;
testmode?: boolean;
};

export type PageParameters = ContextParameters &
PaginationParameters & {
embed?: CaptureEmbed[];
include?: CaptureInclude;
testmode?: boolean;
};

export type IterateParameters = Omit<PageParameters, 'limit'> & ThrottlingParameter;
2 changes: 1 addition & 1 deletion src/binders/payments/parameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { type IdempotencyParameter, type PaginationParameters, type ThrottlingPa
import type PickOptional from '../../types/PickOptional';

export type CreateParameters = Pick<PaymentData, 'amount' | 'description' | 'redirectUrl' | 'cancelUrl' | 'webhookUrl' | 'customerId' | 'mandateId'> &
PickOptional<PaymentData, 'locale' | 'metadata' | 'sequenceType'> & {
PickOptional<PaymentData, 'locale' | 'metadata' | 'sequenceType' | 'captureMode' | 'captureDelay'> & {
/**
* 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
* the chosen payment method. The parameter enables you to fully integrate the payment method selection into your website.
Expand Down
4 changes: 2 additions & 2 deletions src/createMollieClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,12 +187,12 @@ export default function createMollieClient(options: Options) {
export { createMollieClient };

export { ApiMode, Locale, PaymentMethod, HistoricPaymentMethod, SequenceType } from './data/global';
export { CaptureEmbed } from './data/payments/captures/data';
export { CaptureStatus, CaptureInclude } from './data/payments/captures/data';
export { MandateMethod, MandateStatus } from './data/customers/mandates/data';
export { MethodImageSize, MethodInclude } from './data/methods/data';
export { OrderEmbed, OrderStatus } from './data/orders/data';
export { OrderLineType } from './data/orders/orderlines/OrderLine';
export { PaymentEmbed, PaymentInclude, PaymentStatus } from './data/payments/data';
export { PaymentEmbed, PaymentInclude, PaymentStatus, CaptureMethod } from './data/payments/data';
export { RefundEmbed, RefundStatus } from './data/refunds/data';
export { SubscriptionStatus } from './data/subscriptions/data';
export { ProfileStatus } from './data/profiles/data';
Expand Down
15 changes: 14 additions & 1 deletion src/data/payments/captures/CaptureHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type Payment from '../Payment';
import { type PaymentData } from '../data';
import type Capture from './Capture';
import { type CaptureData } from './data';
import type { Settlement, SettlementData } from '../../settlements/data';

export default class CaptureHelper extends Helper<CaptureData, Capture> {
constructor(networkClient: TransformingNetworkClient, protected readonly links: CaptureData['_links'], protected readonly embedded: Capture['_embedded']) {
Expand All @@ -31,7 +32,7 @@ export default class CaptureHelper extends Helper<CaptureData, Capture> {
}

/**
* Returns the shipment that triggered the capture to be created.
* Returns the shipment that triggered the capture to be created (if any).
*
* @since 3.6.0
*/
Expand All @@ -41,4 +42,16 @@ export default class CaptureHelper extends Helper<CaptureData, Capture> {
if (renege(this, this.getShipment, ...arguments)) return;
return runIf(this.links.shipment, ({ href }) => this.networkClient.get<ShipmentData, Shipment>(href)) ?? undefinedPromise;
}

/**
* Returns the shipment this capture has been settled with (if any).
janpaepke marked this conversation as resolved.
Show resolved Hide resolved
*
* @since 4.1.0
*/
public getSettlement(): Promise<Settlement> | Promise<undefined>;
public getSettlement(callback: Callback<Maybe<Settlement>>): void;
public getSettlement() {
if (renege(this, this.getSettlement, ...arguments)) return;
return runIf(this.links.settlement, ({ href }) => this.networkClient.get<SettlementData, Settlement>(href)) ?? undefinedPromise;
}
}
37 changes: 30 additions & 7 deletions src/data/payments/captures/data.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { type Amount, type ApiMode, type Links, type Url } from '../../global';
import type Model from '../../Model';
import { type PaymentData } from '../data';
import type { PaymentData } from '../data';

export interface CaptureData extends Model<'capture'> {
/**
Expand All @@ -11,33 +11,50 @@ export interface CaptureData extends Model<'capture'> {
* @see https://docs.mollie.com/reference/v2/captures-api/get-capture?path=mode#response
*/
mode: ApiMode;
/**
* The description of the capture.
*
* @see https://docs.mollie.com/reference/v2/captures-api/get-capture?path=description#response
*/
description: string;
/**
* The amount captured.
*
* @see https://docs.mollie.com/reference/v2/captures-api/get-capture?path=amount#response
*/
amount: Amount;
/**
* This optional field will contain the amount that will be settled to your account, converted to the currency your account is settled in. It follows the same syntax as the `amount` property.
* This optional field will contain the approximate amount that will be settled to your account, converted to the currency your account is settled in.
*
* @see https://docs.mollie.com/reference/v2/captures-api/get-capture?path=settlementAmount#response
*/
settlementAmount: Amount;
/**
* The capture's status.
*
* @see https://docs.mollie.com/reference/v2/captures-api/get-capture?path=status#response
*/
status: CaptureStatus;
/**
* The capture's status.
janpaepke marked this conversation as resolved.
Show resolved Hide resolved
*
* @see https://docs.mollie.com/reference/v2/captures-api/get-capture?path=metadata#response
*/
metadata: any;
/**
* The unique identifier of the payment this capture was created for, for example: `tr_7UhSN1zuXS`. The full payment object can be retrieved via the `payment` URL in the `_links` object.
*
* @see https://docs.mollie.com/reference/v2/captures-api/get-capture?path=paymentId#response
*/
paymentId: string;
/**
* The unique identifier of the shipment that triggered the creation of this capture, for example: `shp_3wmsgCJN4U`. The full shipment object can be retrieved via the `shipment` URL in the `_links`
* object.
* The unique identifier of the shipment that triggered the creation of this capture, if applicable. For example: `shp_3wmsgCJN4U`.
*
* @see https://docs.mollie.com/reference/v2/captures-api/get-capture?path=shipmentId#response
*/
shipmentId?: string;
/**
* The unique identifier of the settlement this capture was settled with, for example: `stl_jDk30akdN`. The full settlement object can be retrieved via the `capture` URL in the `_links` object.
* The identifier referring to the settlement this capture was settled with. For example, `stl_BkEjN2eBb`. This field is omitted if the capture is not settled (yet).
*
* @see https://docs.mollie.com/reference/v2/captures-api/get-capture?path=settlementId#response
*/
Expand All @@ -59,11 +76,17 @@ export interface CaptureData extends Model<'capture'> {
};
}

export enum CaptureEmbed {
export enum CaptureStatus {
pending = 'pending',
succeeded = 'succeeded',
failed = 'failed',
}

export enum CaptureInclude {
payment = 'payment',
}

export interface CaptureLinks extends Links {
interface CaptureLinks extends Links {
/**
* The API resource URL of the payment the capture belongs to.
*
Expand Down
35 changes: 35 additions & 0 deletions src/data/payments/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,36 @@ export interface PaymentData extends Model<'payment'> {
* @see https://docs.mollie.com/reference/v2/payments-api/get-payment?path=metadata#response
*/
metadata: unknown;
/**
* **Only relevant if you wish to manage authorization and capturing separately.**
*
* By default, the customer's card or bank account is immediately charged when they complete the payment.
*
* Some payment methods also allow placing a hold on the card or bank account. This hold or 'authorization' can then at a later point either be 'captured' or canceled.
*
* To enable this way of working, set the capture mode to `manual` and capture the payment manually using the `paymentCaptures.create` API.
*/
captureMode?: CaptureMethod;
/**
* **Only relevant if you wish to manage authorization and capturing separately.**
*
* Some payment methods allow placing a hold on the card or bank account. This hold or 'authorization' can then at a later point either be 'captured' or canceled.
*
* By default, we charge the customer's card or bank account immediately when they complete the payment. If you set a capture delay however, we will delay the automatic capturing of the payment for the specified amount of time. For example `8 hours` or `2 days`.
*
* To schedule an automatic capture, the `captureMode` must be set to `automatic`.
*
* The maximum delay is 7 days (168 hours).
*
* Possible values: `... hours`, `... days`
*/
captureDelay?: string;
/**
* **Only relevant if you wish to manage authorization and capturing separately.**
*
* Indicates the date before which the payment needs to be captured, in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. From this date onwards we can no longer guarantee a successful capture. The parameter is omitted if the payment is not authorized (yet).
*/
captureBefore?: string;
/**
* The customer's locale, either forced on creation by specifying the `locale` parameter, or detected by us during checkout. Will be a full locale, for example `nl_NL`.
*
Expand Down Expand Up @@ -877,6 +907,11 @@ export enum PaymentEmbed {
captures = 'captures',
}

export enum CaptureMethod {
automatic = 'automatic',
manual = 'manual',
}

export interface GiftCard {
/**
* The ID of the gift card brand that was used during the payment.
Expand Down
4 changes: 4 additions & 0 deletions src/data/settlements/data.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type Nullable from '../../types/Nullable';
import type Seal from '../../types/Seal';
import { type Amount, type Links, type Url } from '../global';
import type Model from '../Model';
import type SettlementHelper from './SettlementHelper';

export interface SettlementData extends Model<'settlement'> {
/**
Expand Down Expand Up @@ -54,6 +56,8 @@ export interface SettlementData extends Model<'settlement'> {
_links: SettlementLinks;
}

export type Settlement = Seal<SettlementData, SettlementHelper>;

interface Period {
/**
* An array of revenue objects containing the total revenue for each payment method during this period. Each object has the following fields.
Expand Down
Loading
Loading