Skip to content

Commit

Permalink
Merge pull request #371 from janpaepke/feature/captures
Browse files Browse the repository at this point in the history
Add captures API endpoints
  • Loading branch information
fjbender authored Sep 30, 2024
2 parents 1fa6177 + 37e0d0a commit a73f311
Show file tree
Hide file tree
Showing 10 changed files with 269 additions and 96 deletions.
26 changes: 19 additions & 7 deletions src/binders/payments/captures/PaymentCapturesBinder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import assertWellFormedId from '../../../plumbing/assertWellFormedId';
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 @@ -19,11 +19,25 @@ 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;
assertWellFormedId(paymentId, 'payment');
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 @@ -41,8 +55,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 @@ -59,8 +72,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 @@ -6,7 +6,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 settlement this capture has been settled with (if any).
*
* @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;
}
}
42 changes: 34 additions & 8 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 @@ -12,32 +12,52 @@ export interface CaptureData extends Model<'capture'> {
*/
mode: ApiMode;
/**
* The amount captured.
* The description of the capture.
*
* @see https://docs.mollie.com/reference/v2/captures-api/get-capture?path=description#response
*/
description: string;
/**
* The amount captured. If no amount is provided, the full authorized amount is 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.
*
* Since the field contains an estimated amount during capture processing, it may change over time. To retrieve accurate settlement amounts we recommend using the List balance transactions endpoint instead.
*
* @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;
/**
* Provide any data you like, for example a string or a JSON object. We will save the data alongside the entity. Whenever you fetch the entity with our API, we will also include the metadata.
* You can use up to approximately 1kB.
*
* @see https://docs.mollie.com/reference/v2/captures-api/get-capture?path=metadata#response
*/
metadata: unknown;
/**
* 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 +79,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

0 comments on commit a73f311

Please sign in to comment.