[Tech Spec] Ppro — CreateClientAuthenticationToken
- Dominant language
- Python
- Stars
- 4
- Forks
- 6
- PR merge metrics
- No merged PRs in 30d
Description
# Ppro — CreateClientAuthenticationToken
**Complexity:** low
**Generated by:** Grace pipeline run `run-2026-05-13T09-40-37-723Z-6e4be6`
## Summary
Implement CreateClientAuthenticationToken payment method for Ppro connector. Technical Specification: Ppro — CreateClientAuthenticationToken
## Scope
# Technical Specification: Ppro — CreateClientAuthenticationToken
## 1. Connector Profile
| Field | Value |
|---|---|
| **Connector Name** | Ppro |
| **Primary Flow Scope** | Alternative Payment Methods (APMs) — redirect-based wallet, bank, and local payment flows |
| **API Family** | REST / JSON (camelCase request/response bodies) |
| **Production Host** | `https://api.eu.ppro.com` |
| **Sandbox Host** | `https://api.sandbox.eu.ppro.com` |
| **Currency Unit** | Minor (integer, e.g. 1000 = 10.00 EUR) |
---
## 2. Authentication
| Field | Value |
|---|---|
| **Scheme** | HTTP Bearer Token |
| **Authorization Header** | `Authorization: Bearer {api_key}` |
| **Merchant Identity Header** | `Merchant-Id: {merchant_id}` |
| **Idempotency Header** | `Request-Idempotency-Key: {connector_request_reference_id}` |
**Credentials required:**
- `api_key` (Secret string) — Bearer token issued by PPRO
- `merchant_id` (Secret string) — Merchant identifier for multi-tenant routing
**Implementation notes:**
- All three headers (`Authorization`, `Merchant-Id`, `Request-Idempotency-Key`) are required on every payment-charge request.
- The `Request-Idempotency-Key` must be unique per attempt; reusing the same key on a POST replays the original response.
---
## 3. Supported Flows
| Flow | HTTP | Path | Notes |
|---|---|---|---|
| **Authorize** | `POST` | `/v1/payment-charges` | Creates a payment charge; returns redirect URL via `authenticationMethods[].details.requestUrl` |
| **Capture** | `POST` | `/v1/payment-charges/{id}/captures` | Finalises authorized payment |
| **PSync** | `GET` | `/v1/payment-charges/{id}` | Retrieves current charge status |
| **Refund** | `POST` | `/v1/payment-charges/{id}/refunds` | Initiates partial or full refund |
| **RSync** | `GET` | `/v1/payment-charges/{id}` | Retrieves charge (used to sync refund status) |
| **Void** | `POST` | `/v1/payment-charges/{id}/voids` | Cancels an authorized charge |
| **SetupMandate** | `POST` | `/v1/payment-agreements
## Out of Scope
Not specified in techspec
## Technical Constraints
- Follow existing connector patterns in the codebase
---
## Full Tech Spec
# Technical Specification: Ppro — CreateClientAuthenticationToken
## 1. Connector Profile
| Field | Value |
|---|---|
| **Connector Name** | Ppro |
| **Primary Flow Scope** | Alternative Payment Methods (APMs) — redirect-based wallet, bank, and local payment flows |
| **API Family** | REST / JSON (camelCase request/response bodies) |
| **Production Host** | `https://api.eu.ppro.com` |
| **Sandbox Host** | `https://api.sandbox.eu.ppro.com` |
| **Currency Unit** | Minor (integer, e.g. 1000 = 10.00 EUR) |
---
## 2. Authentication
| Field | Value |
|---|---|
| **Scheme** | HTTP Bearer Token |
| **Authorization Header** | `Authorization: Bearer {api_key}` |
| **Merchant Identity Header** | `Merchant-Id: {merchant_id}` |
| **Idempotency Header** | `Request-Idempotency-Key: {connector_request_reference_id}` |
**Credentials required:**
- `api_key` (Secret string) — Bearer token issued by PPRO
- `merchant_id` (Secret string) — Merchant identifier for multi-tenant routing
**Implementation notes:**
- All three headers (`Authorization`, `Merchant-Id`, `Request-Idempotency-Key`) are required on every payment-charge request.
- The `Request-Idempotency-Key` must be unique per attempt; reusing the same key on a POST replays the original response.
---
## 3. Supported Flows
| Flow | HTTP | Path | Notes |
|---|---|---|---|
| **Authorize** | `POST` | `/v1/payment-charges` | Creates a payment charge; returns redirect URL via `authenticationMethods[].details.requestUrl` |
| **Capture** | `POST` | `/v1/payment-charges/{id}/captures` | Finalises authorized payment |
| **PSync** | `GET` | `/v1/payment-charges/{id}` | Retrieves current charge status |
| **Refund** | `POST` | `/v1/payment-charges/{id}/refunds` | Initiates partial or full refund |
| **RSync** | `GET` | `/v1/payment-charges/{id}` | Retrieves charge (used to sync refund status) |
| **Void** | `POST` | `/v1/payment-charges/{id}/voids` | Cancels an authorized charge |
| **SetupMandate** | `POST` | `/v1/payment-agreements` | Creates a recurring payment agreement |
| **RepeatPayment** | `POST` | `/v1/payment-agreements/{agr_id}/payment-charges` | Charges an existing agreement |
| **CreateClientAuthenticationToken** | `POST` | `/v1/payment-charges` | Creates a pre-initiated charge; returns `charge_id` and `redirect_url` for client-side authentication |
| **Webhooks** | — | Configured via `webhooksUrl` in request body | Delivers async event notifications |
---
## 4. Request Schema Highlights
### CreateClientAuthenticationToken — `POST /v1/payment-charges`
```json
{
"paymentMethod": "BANCONTACT",
"paymentMedium": "ECOMMERCE",
"merchantPaymentChargeReference": "ref_001",
"amount": {
"currency": "EUR",
"value": 1000
},
"consumer": {
"name": "John Doe",
"email": "john@example.com",
"country": "BE"
},
"authenticationSettings": [
{
"type": "REDIRECT",
"settings": {
"returnUrl": "https://merchant.com/return"
}
}
]
}
```
| Field | Type | Required | Notes |
|---|---|---|---|
| `paymentMethod` | String (enum) | Yes | One of: `BANCONTACT`, `IDEAL`, `TRUSTLY`, `BLIK`, `ALIPAY`, `WECHATPAY`, `MBWAY`, `SATISPAY`, `WERO`, `UPI` |
| `paymentMedium` | String (enum) | Yes | `ECOMMERCE` \| `MOTO` \| `POS` — defaults to `ECOMMERCE` for client auth flows |
| `merchantPaymentChargeReference` | String | Yes | Idempotency key; maps to `connector_request_reference_id` |
| `amount.currency` | String (ISO 4217) | Yes | Three-letter currency code |
| `amount.value` | Integer | Yes | Minor unit amount |
| `consumer.name` | String | Optional | Billing full name |
| `consumer.email` | String | Optional | Billing email |
| `consumer.country` | String | Optional | ISO 3166-1 alpha-2 country code from billing address |
| `consumer.merchantConsumerReference` | String | Optional | Required for Trustly; scopes agreement to consumer |
| `authenticationSettings[].type` | String (enum) | Optional | `REDIRECT` — instructs PPRO to return a redirect URL |
| `authenticationSettings[].settings.returnUrl` | String (URL) | Optional | Return URL after consumer completes redirect flow |
**Idempotency:** `Request-Idempotency-Key` header (equal to `merchantPaymentChargeReference`) provides request-level idempotency.
---
## 5. Response Schema Highlights
### Success Response (HTTP 200/201)
```json
{
"id": "chg_abc123",
"status": "AUTHENTICATION_PENDING",
"authenticationMethods": [
{
"type": "REDIRECT",
"details": {
"requestUrl": "https://redirect.ppro.com/pay/chg_abc123"
}
}
],
"failure": null
}
```
| Field | Type | Notes |
|---|---|---|
| `id` | String | Connector transaction ID (charge ID) — stored as `charge_id` in `PproClientAuthenticationResponse` |
| `status` | String (enum) | See status values below |
| `authenticationMethods[].type` | String | `REDIRECT` (or `SCAN_CODE`, `MULTI_FACTOR`, `APP_NOTIFICATION`, `APP_INTENT`) |
| `authenticationMethods[].details.requestUrl` | String | Redirect URL for client-side authentication — stored as `redirect_url` |
| `failure.failureType` | String | Category of failure (e.g., `FRAUD`, `INSUFFICIENT_FUNDS`) |
| `failure.failureCode` | String | Optional machine-readable failure code |
| `failure.failureMessage` | String | Human-readable failure description |
### Payment Status Values
| Status | Mapped AttemptStatus | Notes |
|---|---|---|
| `AUTHORIZATION_PROCESSING` | `Pending` | |
| `AUTHENTICATION_PENDING` | `AuthenticationPending` | Consumer must complete redirect |
| `AUTHORIZATION_ASYNC` | `Authorized` | |
| `CAPTURE_PENDING` | `Authorized` | |
| `CAPTURE_PROCESSING` | `Authorized` | |
| `CAPTURED` / `SUCCESS` | `Charged` | Terminal success |
| `FAILED` / `DISCARDED` / `REJECTED` / `DECLINED` | `Failure` | Terminal failure |
| `VOIDED` | `Voided` | |
| `REFUND_SETTLED` / `REFUNDED` | `Charged` | Post-refund, charge remains Charged |
---
## 6. Error Handling
| HTTP | `status` field | Cause |
|---|---|---|
| `400` | 400 | Malformed request body — missing required fields or invalid enum values |
| `401` | 401 | Invalid or missing `Authorization` Bearer token |
| `403` | 403 | Valid token but insufficient permissions for this merchant |
| `409` | 409 | Duplicate `merchantPaymentChargeReference` — idempotency key collision |
| `422` | 422 | Unprocessable entity — e.g., unsupported `paymentMethod` for this merchant account |
| `429` | 429 | Rate limit exceeded |
| `500` | 500 | PPRO internal server error |
**Error Response Format:**
```json
{
"status": 400,
"failureMessage": "paymentMethod is required"
}
```
Mapped via `PproErrorResponse { status: u16, failure_message: String }`.
---
## 7. Webhooks / Async Notifications
### Subscription
Webhooks are subscribed per-payment by setting `webhooksUrl` in the payment charge request body.
### Delivery Format
CloudEvents 1.0 envelope (JSON):
```json
{
"specversion": "1.0",
"type": "PAYMENT_CHARGE_SUCCESS",
"source": "ppro",
"id": "event_001",
"time": "2024-01-01T00:00:00Z",
"data": {
"charge": {
"id": "chg_abc123",
"status": "SUCCEEDED",
"amount": 1000,
"currency": "EUR"
}
}
}
```
### Webhook Event Types
| Event Type | Notes |
|---|---|
| `PAYMENT_CHARGE_AUTHORIZATION_SUCCEEDED` | Authorization successful |
| `PAYMENT_CHARGE_SUCCESS` | Capture complete |
| `PAYMENT_CHARGE_AUTHORIZATION_FAILED` | Authorization failed |
| `PAYMENT_CHARGE_FAILED` | Capture/general failure |
| `PAYMENT_CHARGE_DISCARDED` | Charge discarded |
| `PAYMENT_CHARGE_CAPTURE_SUCCEEDED` | Capture confirmed |
| `PAYMENT_CHARGE_CAPTURE_FAILED` | Capture failed |
| `PAYMENT_CHARGE_VOID_SUCCEEDED` | Void successful |
| `PAYMENT_CHARGE_VOID_FAILED` | Void failed |
| `PAYMENT_CHARGE_REFUND_SUCCEEDED` | Refund settled |
| `PAYMENT_CHARGE_REFUND_FAILED` | Refund failed |
| `PAYMENT_AGREEMENT_ACTIVE` | Recurring agreement established |
| `PAYMENT_AGREEMENT_FAILED` | Recurring agreement failed |
| `PAYMENT_AGREEMENT_REVOKED_BY_CONSUMER` | Consumer revoked mandate |
| `PAYMENT_AGREEMENT_REVOKED_BY_MERCHANT` | Merchant revoked mandate |
| `PAYMENT_AGREEMENT_REVOKED_BY_PROVIDER` | PPRO revoked mandate |
### Signature Verification
- Header: `Webhook-Signature` (hex-encoded)
- Algorithm: SHA-256 HMAC over `body + "." + webhook_secret`
- Implementation: `crypto::Sha256.verify_signature(&[], &expected_signature, &message)`
### Retry Policy
Documented: PPRO retries failed webhook deliveries with exponential backoff. Exact retry count and backoff interval not publicly documented.
### Gaps
- No per-event subscription filtering — all events are delivered to `webhooksUrl`
- Webhook secret is configured out-of-band (not returned in API responses)
---
## 8. References
| Resource | URL |
|---|---|
| PPRO Sandbox API | `https://api.sandbox.eu.ppro.com` |
| PPRO Production API | `https://api.eu.ppro.com` |
| Ppro Connector Implementation | `crates/integrations/connector-integration/src/connectors/ppro.rs` |
| Ppro Transformers | `crates/integrations/connector-integration/src/connectors/ppro/transformers.rs` |
| Field Probe Data | `data/field_probe/ppro.json` |
| Connector Docs | `docs-generated/connectors/ppro.md` |
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with crates/integrations/connector-integration/src/connectors/ppro.rs and its transformers.rs, then compare existing connector patterns for CreateClientAuthenticationToken. Use data/field_probe/ppro.json and the request and response schemas to verify the Ppro charge flow, including the charge ID and redirect URL; done means the documented flow is implemented consistently with the Ppro connector.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- payments
- Issue type
- Feature
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 65/100