juspay / juspay/grace

[Tech Spec] Ppro — CreateClientAuthenticationToken

Open
#154 0 comments 0 reactions 0 assignees View on GitHub
connector-integration
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-13T11-34-52-053Z-f3ed51`

## Summary

Implement CreateClientAuthenticationToken payment method for Ppro connector. Ppro CreateClientAuthenticationToken — Technical Specification

## Scope

# Ppro CreateClientAuthenticationToken — Technical Specification

## 1. Connector Profile

| Field | Value |
|---|---|
| **Connector Name** | Ppro |
| **Primary Flow Scope** | CreateClientAuthenticationToken (MerchantAuthenticationService) |
| **API Family** | REST/JSON over HTTPS |
| **Production Host** | `https://api.eu.ppro.com` |
| **Sandbox Host** | `https://api.sandbox.eu.ppro.com` |
| **Currency Unit** | Minor (amounts expressed as integer minor units, e.g. 1000 = €10.00) |

Ppro is a white-label payment service provider and local payment method aggregator. It routes payments across BANCONTACT, IDEAL, BLIK, ALIPAY, WECHATPAY, MBWAY, SATISPAY, WERO, TRUSTLY, UPI, and others through a single API. The `CreateClientAuthenticationToken` flow initialises a Ppro checkout session that returns a `sessionId` (and optional `sessionToken`) used to mount Ppro's hosted checkout page or client-side SDK.

---

## 2. Authentication

| Field | Value |
|---|---|
| **Scheme** | Bearer Token + Merchant-Id header |
| **Authorization header** | `Authorization: Bearer {api_key}` |
| **Merchant header** | `Merchant-Id: {merchant_id}` |
| **Content-Type** | `application/json` |
| **Credentials required** | `api_key` (Secret), `merchant_id` (Secret) |
| **Optional idempotency key** | `Request-Idempotency-Key` header (UUID, optional per request) |

**Implementation notes:**
- The `api_key` is issued per merchant by Ppro and must be kept server-side.
- `merchant_id` is a Ppro-assigned identifier; both are present in `ConnectorSpecificConfig::Ppro`.
- All Ppro endpoints require both headers; omitting either yields a `401 Unauthorized`.

---

## 3. Supported Flows

| Flow | HTTP | Path | Notes |
|---|---|---|---|
| **CreateClientAuthenticationToken** | POST | `/v1/checkout-sessions` | Creates a checkout session; returns `sessionId` + optional `sessionToken` for SDK init |
| Authorize | POST | `/v1/payment-charges` | Direct payment charge creation |
| PSync | GET | `/v1/paym

## Out of Scope

Not specified in techspec

## Technical Constraints

- Follow existing connector patterns in the codebase

---

## Full Tech Spec

# Ppro CreateClientAuthenticationToken — Technical Specification

## 1. Connector Profile

| Field | Value |
|---|---|
| **Connector Name** | Ppro |
| **Primary Flow Scope** | CreateClientAuthenticationToken (MerchantAuthenticationService) |
| **API Family** | REST/JSON over HTTPS |
| **Production Host** | `https://api.eu.ppro.com` |
| **Sandbox Host** | `https://api.sandbox.eu.ppro.com` |
| **Currency Unit** | Minor (amounts expressed as integer minor units, e.g. 1000 = €10.00) |

Ppro is a white-label payment service provider and local payment method aggregator. It routes payments across BANCONTACT, IDEAL, BLIK, ALIPAY, WECHATPAY, MBWAY, SATISPAY, WERO, TRUSTLY, UPI, and others through a single API. The `CreateClientAuthenticationToken` flow initialises a Ppro checkout session that returns a `sessionId` (and optional `sessionToken`) used to mount Ppro's hosted checkout page or client-side SDK.

---

## 2. Authentication

| Field | Value |
|---|---|
| **Scheme** | Bearer Token + Merchant-Id header |
| **Authorization header** | `Authorization: Bearer {api_key}` |
| **Merchant header** | `Merchant-Id: {merchant_id}` |
| **Content-Type** | `application/json` |
| **Credentials required** | `api_key` (Secret), `merchant_id` (Secret) |
| **Optional idempotency key** | `Request-Idempotency-Key` header (UUID, optional per request) |

**Implementation notes:**
- The `api_key` is issued per merchant by Ppro and must be kept server-side.
- `merchant_id` is a Ppro-assigned identifier; both are present in `ConnectorSpecificConfig::Ppro`.
- All Ppro endpoints require both headers; omitting either yields a `401 Unauthorized`.

---

## 3. Supported Flows

| Flow | HTTP | Path | Notes |
|---|---|---|---|
| **CreateClientAuthenticationToken** | POST | `/v1/checkout-sessions` | Creates a checkout session; returns `sessionId` + optional `sessionToken` for SDK init |
| Authorize | POST | `/v1/payment-charges` | Direct payment charge creation |
| PSync | GET | `/v1/payment-charges/{id}` | Fetch charge status by Ppro charge ID |
| Capture | POST | `/v1/payment-charges/{id}/captures` | Manual capture of an authorised charge |
| Void | POST | `/v1/payment-charges/{id}/voids` | Cancel/void an authorised charge |
| Refund | POST | `/v1/payment-charges/{id}/refunds` | Create a refund against a captured charge |
| RSync | GET | `/v1/payment-charges/{id}` | Fetch refund status (same endpoint, check `refunds[]`) |
| SetupMandate | POST | `/v1/payment-agreements` | Create a recurring payment agreement |
| RepeatPayment | POST | `/v1/payment-agreements/{id}/payment-charges` | Charge against an existing agreement |
| Webhooks | POST | _(merchant-configured URL)_ | PPRO pushes CloudEvents-formatted events |

---

## 4. Request Schema Highlights — CreateClientAuthenticationToken

### Endpoint
```
POST {base_url}/v1/checkout-sessions
Content-Type: application/json
Authorization: Bearer {api_key}
Merchant-Id: {merchant_id}
```

### Request Body
```json
{
"amount": {
"currency": "EUR",
"value": 1000
},
"paymentMethod": "IDEAL",
"merchantPaymentChargeReference": "order-ref-001",
"consumer": {
"email": "user@example.com",
"country": "NL"
},
"authenticationSettings": [
{
"type": "REDIRECT",
"settings": {
"returnUrl": "https://merchant.example.com/return"
}
}
],
"webhooksUrl": "https://merchant.example.com/webhook"
}
```

### Field Reference

| Field | Type | Required | Notes |
|---|---|---|---|
| `amount.currency` | string (ISO 4217) | yes | e.g. `"EUR"`, `"USD"` |
| `amount.value` | integer (minor units) | yes | e.g. `1000` = €10.00 |
| `paymentMethod` | string enum | no | Optional at session creation; can be fixed or left open for SDK to select. One of: `BANCONTACT`, `IDEAL`, `BLIK`, `ALIPAY`, `WECHATPAY`, `MBWAY`, `SATISPAY`, `WERO`, `TRUSTLY`, `UPI` |
| `merchantPaymentChargeReference` | string | yes | Merchant-generated unique reference for the payment |
| `consumer.email` | string (email) | no | Consumer's email address |
| `consumer.country` | string (ISO 3166-1 alpha-2) | no | Consumer's country code |
| `consumer.name` | string | no | Consumer's full name |
| `consumer.merchantConsumerReference` | string | no | Required by certain PMs (e.g. Trustly) for consumer identification |
| `authenticationSettings[].type` | enum | no | `REDIRECT` (currently supported); future: `SCAN_CODE`, `MULTI_FACTOR`, `APP_NOTIFICATION`, `APP_INTENT` |
| `authenticationSettings[].settings.returnUrl` | string (URL) | no | Required when type is `REDIRECT`; consumer is redirected here after authentication |
| `webhooksUrl` | string (URL) | no | Override webhook delivery URL for this session |
| `paymentDescriptor` | string | no | Soft descriptor shown on consumer statement |
| `paymentMedium` | enum | no | `ECOMMERCE` (default), `MOTO`, `POS` |

**Idempotency:** Include `Request-Idempotency-Key: ` header to safely retry session creation.

---

## 5. Response Schema Highlights

### Success Response (HTTP 200 / 201)
```json
{
"sessionId": "sess_abc123def456",
"sessionToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresAt": "2026-05-13T12:30:00Z",
"checkoutUrl": "https://checkout.ppro.com/session/sess_abc123def456",
"status": "CREATED"
}
```

### Key Response Fields

| Field | Type | Description |
|---|---|---|
| `sessionId` | string | Unique Ppro session identifier; used as the `ConnectorSpecific` response `session_id` |
| `sessionToken` | string (JWT, Secret) | Short-lived token for client-side SDK initialisation; may be absent for redirect-only flows |
| `expiresAt` | string (ISO 8601) | Session expiry timestamp |
| `checkoutUrl` | string (URL) | Full redirect URL to Ppro's hosted checkout page; client should redirect to this |
| `status` | string enum | `CREATED` on success |

### UCS Response Mapping
The `sessionId` and `sessionToken` are surfaced as `ClientAuthenticationTokenData::ConnectorSpecific(ConnectorSpecificClientAuthenticationResponse::Ppro(...))` containing:
- `session_id: String` — the Ppro `sessionId`
- `session_token: Secret` — the Ppro `sessionToken` (if present)
- `checkout_url: Option` — the `checkoutUrl` for redirect flows

---

## 6. Error Handling

### Error Response Body
```json
{
"status": 400,
"failureMessage": "Invalid paymentMethod value: UNSUPPORTED"
}
```

### HTTP Status → Error Cause Mapping

| HTTP | `status` (body) | Cause |
|---|---|---|
| 400 | 400 | Malformed request; invalid field value, missing required field |
| 401 | 401 | Missing or invalid `Authorization` header; invalid `api_key` |
| 403 | 403 | `Merchant-Id` not authorised for the requested payment method or operation |
| 404 | 404 | Referenced resource (session, charge) not found |
| 409 | 409 | Idempotency conflict — same `Request-Idempotency-Key` with different request body |
| 422 | 422 | Semantically invalid request (e.g. amount below minimum, unsupported currency for PM) |
| 429 | 429 | Rate limit exceeded |
| 500 | 500 | Ppro internal server error; safe to retry with backoff |

**UCS Error Parsing:** The existing `PproErrorResponse { status: u16, failure_message: String }` struct handles the error body. The `status` field from the body maps to `ErrorResponse.code`; `failure_message` maps to `ErrorResponse.message`.

---

## 7. Webhooks / Async Notifications

### Subscription
Webhook delivery URL is configured per-merchant in the Ppro merchant portal, or overridden per-request via the `webhooksUrl` field in the session creation request.

### Delivery Format
Ppro delivers CloudEvents v1.0-formatted JSON payloads over HTTP POST:
```json
{
"specversion": "1.0",
"type": "PAYMENT_CHARGE_SUCCESS",
"source": "ppro",
"id": "evt_abc123",
"time": "2026-05-13T10:00:00Z",
"data": {
"charge": {
"id": "chg_xyz789",
"status": "SUCCEEDED",
"amount": 1000,
"currency": "EUR"
}
}
}
```

### Signature Verification
- **Algorithm:** HMAC-SHA-256
- **Header:** `Webhook-Signature` (hex-encoded digest)
- **Message construction:** `{raw_body}.{webhook_secret_bytes}` (body bytes + `.` + secret bytes)
- **Verification:** `SHA256(body + "." + secret) == hex_decode(Webhook-Signature header)`

### Event Types

| Event type (`type` field) | UCS EventType |
|---|---|
| `PAYMENT_CHARGE_AUTHORIZATION_SUCCEEDED` | `PaymentIntentAuthorizationSuccess` |
| `PAYMENT_CHARGE_SUCCESS` | `PaymentIntentAuthorizationSuccess` |
| `PAYMENT_CHARGE_AUTHORIZATION_FAILED` | `PaymentIntentFailure` |
| `PAYMENT_CHARGE_FAILED` | `PaymentIntentFailure` |
| `PAYMENT_CHARGE_DISCARDED` | `PaymentIntentFailure` |
| `PAYMENT_CHARGE_CAPTURE_SUCCEEDED` | `PaymentIntentCaptureSuccess` |
| `PAYMENT_CHARGE_CAPTURE_FAILED` | `PaymentIntentCaptureFailure` |
| `PAYMENT_CHARGE_VOID_SUCCEEDED` | `PaymentIntentCancelled` |
| `PAYMENT_CHARGE_VOID_FAILED` | `PaymentIntentCancelFailure` |
| `PAYMENT_CHARGE_REFUND_SUCCEEDED` | `RefundSuccess` |
| `PAYMENT_CHARGE_REFUND_FAILED` | `RefundFailure` |
| `PAYMENT_AGREEMENT_ACTIVE` | `MandateActive` |
| `PAYMENT_AGREEMENT_FAILED` | `MandateFailed` |
| `PAYMENT_AGREEMENT_REVOKED_*` | `MandateRevoked` |

### Retry Policy
Ppro retries failed webhook deliveries with exponential backoff. The merchant should respond with HTTP 2xx within 5 seconds to acknowledge receipt.

### Documented Gaps
- The `CreateClientAuthenticationToken` flow (checkout session) does not have a dedicated session-status webhook; session lifecycle (expiry) is not notified via webhook.
- The `sessionToken` expiry is returned synchronously in the session response (`expiresAt`); no async notification is sent when it expires.

---

## 8. References

| Source | Details |
|---|---|
| Existing Ppro connector implementation | `crates/integrations/connector-integration/src/connectors/ppro.rs` |
| Ppro transformer types | `crates/integrations/connector-integration/src/connectors/ppro/transformers.rs` |
| ClientAuthenticationToken domain types | `crates/types-traits/domain_types/src/connector_types.rs` (lines 1603–1620, 3518–3590) |
| ConnectorSpecificConfig::Ppro | `crates/types-traits/domain_types/src/router_data.rs` (lines 275–279) |
| Sandbox base URL | `config/development.toml` — `ppro.base_url = "https://api.sandbox.eu.ppro.com"` |
| Nexinets ClientAuthenticationToken reference impl | `crates/integrations/connector-integration/src/connectors/nexinets.rs` (lines 520–540) |
| Revolut ClientAuthenticationToken reference impl | `crates/integrations/connector-integration/src/connectors/revolut.rs` (lines 295–320) |
| Ppro API Documentation (public) | `https://docs.ppro.com/api/` |
| Ppro Checkout Session API | `https://docs.ppro.com/api/#tag/Checkout-Sessions` |

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by locating the existing connector patterns for MerchantAuthenticationService and the Ppro CreateClientAuthenticationToken flow. Implement the POST /v1/checkout-sessions request, authentication, response mapping, and error handling described in the specification; done means the session_id, session_token, and checkout_url reach the corresponding connector-specific response and the documented failures map correctly.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api, backend, payments
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.