Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions content/1.guides/7.webhooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ description: "In this guide, we will look at how to register and consume webhook

To register a new webhook, you need to have a URL in your app that Vatly can call. You can configure a new webhook from the Vatly dashboard under [API settings](#). Give your webhook a name, pick the [events](#event-types) you want to listen for, and add your URL.

You can also manage the endpoint programmatically with the [Webhook Endpoints API](/api-reference/webhook-endpoints) — handy for provisioning from CI / infrastructure-as-code or pointing an ephemeral preview environment at its own public URL. There is at most one endpoint per mode (test and live), and you supply the signing secret when you register it.

Now, whenever something of interest happens in your app, a webhook is fired off by Vatly. In the next section, we'll look at how to consume webhooks.

::note
Expand Down Expand Up @@ -82,12 +84,18 @@ The `eventName` field identifies what happened. The available events are:
| `subscription.cancellation_grace_period_completed` | The grace period after a cancellation ended. |
| `subscription.resumed` | A canceled subscription was resumed during its grace period. |
| `subscription.billing_updated` | A subscription's billing details changed — in practice the mandate (payment method on file), e.g. a refreshed masked payment-method identifier. |
| `subscription.updated` | A subscription changed immediately (effective now, not at the next cycle) — a plan, price, interval, or quantity change. `object` is the subscription with its new values. |
| `subscription.update_scheduled` | A plan, price, or quantity change was scheduled to take effect at the next billing cycle. `object` is the subscription as it is *today*; the target values are carried in `object.scheduledUpdate`. |
| `webhook.setup` | A verification call Vatly sends when you register a webhook endpoint or change its URL. See the note below. |

::note
**`object` is the affected resource, keyed by its own `resource` field** (`order`, `chargeback`, `refund`, `subscription`, `checkout`, or `webhook`). For chargeback events this differs from `entityType`: `order.chargeback_received` / `order.chargeback_reversed` carry `entityType: order` (the order the chargeback belongs to) but the `object` is a Chargeback.
::

::note
**`subscription.update_scheduled`** carries the subscription's **current** state in `object`, plus the future target values in `object.scheduledUpdate` (`subscriptionPlanId`, `name`, `description`, `basePrice`, `quantity`, `interval`, `intervalCount`). `scheduledUpdate` is present only on this delivery — it is never returned by the REST API. The matching `subscription.updated` event (for changes that take effect immediately) has no `scheduledUpdate`; its `object` already reflects the new values.
::

::note
**`webhook.setup`** is delivered when you register an endpoint (or change its URL) to confirm it's reachable. It arrives as a normal, signed delivery (`entityType: webhook`, `object` is the secret-free endpoint config), so there's nothing special to parse — just acknowledge it with a `2xx`. Unlike event deliveries, the setup call is a one-shot verification check: a non-`2xx` response fails endpoint registration and is **not** retried. It is also the one event that is never persisted, so it does not appear on the [Get webhook event](/api-reference/webhook-events) endpoint.
::
Expand Down Expand Up @@ -149,12 +157,11 @@ That endpoint returns the event metadata plus the exact resource snapshot that w

## Testing webhook flows

For recurring billing flows, Vatly also exposes test helpers so you can trigger webhook-producing scenarios in test mode:
For recurring billing flows, Vatly also exposes a test helper so you can trigger webhook-producing scenarios in test mode:

- [Fast-forward subscription renewal](/api-reference/test-helpers)
- [Simulate a mandated payment failure](/api-reference/test-helpers)

This is useful when you want to verify renewals, failure handling, retries, and dunning behavior without waiting for real billing dates.
This is useful when you want to verify renewals and the events they produce without waiting for real billing dates.

---

Expand Down
59 changes: 1 addition & 58 deletions content/1.guides/9.testing.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "Testing"
description: "Use test helper endpoints to simulate billing events like subscription renewals and payment failures in sandbox mode."
description: "Use test helper endpoints to simulate billing events like subscription renewals in sandbox mode."
---

## Overview
Expand Down Expand Up @@ -99,60 +99,3 @@ Returns the updated subscription with new renewal dates:
}
}
```

## Simulate payment failure

Simulates a failed mandated payment, allowing you to test payment failure handling, retry logic, dunning flows, and webhook notifications.

### Request

`POST /v1/test-helpers/mandated-payments/{transactionId}/simulate-failure`

| Parameter | Type | In | Description |
|-----------|------|----|-------------|
| `transactionId` | string | path | ID of the mandated payment transaction to fail |
| `reason` | string | body | Failure reason (optional, defaults to `general_failure`) |

#### Failure reasons

| Reason | Description |
|--------|-------------|
| `insufficient_funds` | Customer's account has insufficient funds |
| `invalid_mandate` | The payment mandate is invalid |
| `mandate_canceled` | The mandate has been canceled |
| `account_closed` | The customer's account is closed |
| `card_expired` | The customer's card has expired |
| `authentication_failed` | Payment authentication failed |
| `general_failure` | General payment failure (default) |

### Example

::code-group{sync="client"}

```bash [cURL]
curl -X POST https://api.vatly.com/v1/test-helpers/mandated-payments/mollie_mandated_payment_Xk9pQrSvWm4NjLhYbUcP/simulate-failure \
-H "Authorization: Bearer test_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"reason": "insufficient_funds"
}'
```

```php [PHP]
$vatly->testHelpers->simulatePaymentFailure(
'mollie_mandated_payment_Xk9pQrSvWm4NjLhYbUcP',
['reason' => 'insufficient_funds']
);
```

::

### Response

```json
{
"id": "mollie_mandated_payment_Xk9pQrSvWm4NjLhYbUcP",
"status": "failed",
"failureReason": "insufficient_funds"
}
```
65 changes: 65 additions & 0 deletions content/2.api-reference/1.customers.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Below you'll find all properties for the Vatly Customer API resource.
| `resource` | `string` | The resource type. Always `customer`. |
| `testmode` | `boolean` | Whether this resource is in test mode. |
| `email` | `string` | The email address for the customer. |
| `name` | `string \| null` | The customer's display / account-holder name. An identity field used for communication (dunning emails, dashboard) — distinct from, and with no effect on, the billing name on invoices. |
| `createdAt` | `string` | When this customer was created (ISO 8601 format). |
| `metadata` | `object \| null` | Arbitrary key-value metadata for your application. Up to 50 keys, with key names up to 40 characters and values up to 500 characters. |
| `links` | `object` | HATEOAS links to related resources. Contains `self` link. |
Expand Down Expand Up @@ -59,6 +60,7 @@ $customers = $vatly->customers->page();
"resource": "customer",
"testmode": false,
"email": "john.doe@example.com",
"name": "John Doe",
"createdAt": "2024-01-15T10:30:00Z",
"metadata": {
"userId": "user_Qp8kNvBxKw7RjTgYcZaE"
Expand All @@ -75,6 +77,7 @@ $customers = $vatly->customers->page();
"resource": "customer",
"testmode": false,
"email": "jane.smith@acme.com",
"name": "Jane Smith",
"createdAt": "2024-01-10T08:15:00Z",
"metadata": {},
"links": {
Expand Down Expand Up @@ -120,6 +123,7 @@ Customers are uniquely identified by email within each testmode. Creating a cust

| Name | Type | Description |
| --- | --- | --- |
| `name` | `string` | The customer's display / account-holder name (max 255 characters). |
| `metadata` | `object` | Arbitrary key-value metadata for your application. |

::code-group{sync="api"}
Expand Down Expand Up @@ -149,6 +153,7 @@ $vatly->customers->create([
"resource": "customer",
"testmode": false,
"email": "customer@example.com",
"name": "John Doe",
"createdAt": "2024-01-15T10:30:00Z",
"metadata": {
"userId": "user_Qp8kNvBxKw7RjTgYcZaE"
Expand Down Expand Up @@ -193,6 +198,66 @@ $vatly->customers->get('customer_7kBmRtPvXw2NjLhYcZaE');
"resource": "customer",
"testmode": false,
"email": "john.doe@example.com",
"name": "John Doe",
"createdAt": "2024-01-15T10:30:00Z",
"metadata": {
"userId": "user_Qp8kNvBxKw7RjTgYcZaE"
},
"links": {
"self": {
"href": "https://api.vatly.com/v1/customers/customer_7kBmRtPvXw2NjLhYcZaE",
"type": "application/json"
}
}
}
```

::


---

## Update a customer

`PATCH /v1/customers/:id`

Updates a customer's identity fields. Only `name` and `email` may be changed here, and both are optional — send whichever you want to update.

Billing-address details (company name, tax ID, street, city, country, etc.) are **not** supported by this endpoint and are ignored. Amend those through the [hosted billing-update flow](/api-reference/subscriptions#create-billing-update-link), which validates tax-relevant data centrally so invoices stay accurate.

### Optional attributes

| Name | Type | Description |
| --- | --- | --- |
| `name` | `string \| null` | The customer's display / account-holder name (max 255 characters). |
| `email` | `string` | The customer's email address. Must be unique within the merchant's account for the given testmode. |

::code-group{sync="api"}

```bash [cURL]
curl -X PATCH https://api.vatly.com/v1/customers/customer_7kBmRtPvXw2NjLhYcZaE \
-H "Authorization: Bearer live_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"name": "John Doe", "email": "new.email@example.com"}'
```

```php [PHP]
$vatly = new \Vatly\API\VatlyApiClient();
$vatly->setApiKey('live_your_api_key_here');

$customer = $vatly->customers->update('customer_7kBmRtPvXw2NjLhYcZaE', [
'name' => 'John Doe',
'email' => 'new.email@example.com',
]);
```

```json [Response]
{
"id": "customer_7kBmRtPvXw2NjLhYcZaE",
"resource": "customer",
"testmode": false,
"email": "new.email@example.com",
"name": "John Doe",
"createdAt": "2024-01-15T10:30:00Z",
"metadata": {
"userId": "user_Qp8kNvBxKw7RjTgYcZaE"
Expand Down
Loading
Loading