juspay / juspay/grace

[Tech Spec] Ppro — CreateClientAuthenticationToken

Open
#156 1 comment 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-13T15-17-37-349Z-cf8aba`

## Summary

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

## Scope

# Ppro — CreateClientAuthenticationToken Technical Specification

> Connector: Ppro
> Flow: CreateClientAuthenticationToken (MerchantAuthenticationService.CreateClientAuthenticationToken)
> Status: NOT CURRENTLY IMPLEMENTED — this spec defines the target design based on PPRO API patterns
> Generated: 2026-05-13

---

## 1. Connector Profile

| Property | Value |
|---|---|
| Connector name | Ppro |
| Display name | PPRO |
| Description | Global provider of local payment infrastructure (APMs, wallets, bank redirects) |
| Connector type | PaymentGateway |
| Primary flow scope | Authorize, Capture, Void, PSync, Refund, RSync, SetupMandate, RepeatPayment, CreateClientAuthenticationToken (target) |
| API family | REST / JSON |
| Production host | Configurable via `base_url` connector config field (e.g. `https://api.eu.ppro.com`) |
| Sandbox host | `https://api.sandbox.eu.ppro.com` |
| Regional hosts | `base_url` is merchant-configured — PPRO may issue per-region endpoints |
| API versioning | Path-based: `/v1/` prefix |
| Currency unit | Minor (integer) |

---

## 2. Authentication

| Property | Value |
|---|---|
| Scheme | HTTP Bearer Token + Merchant-ID header |
| Credentials required | `api_key` (bearer token), `merchant_id` (header), `base_url` (configurable) |

### Headers required for every request

```
Authorization: Bearer
Merchant-Id:
Content-Type: application/json
```

### Implementation notes

- The `api_key` is peeked from the masked `Secret` and injected as `Bearer ` into the `Authorization` header.
- The `merchant_id` is sent as its own `Merchant-Id` header (not part of the Bearer value).
- For mutating requests (POST), a `Request-Idempotency-Key` header is also required — populated from `connector_request_reference_id`.
- The `base_url` is dynamic (merchant-configured), not hardcoded in the connector — callers may point at sandbox or production.
- Connector config type: `ConnectorSpecificConfig::Ppro { api_key, merchan

## Out of Scope

Not specified in techspec

## Technical Constraints

- Follow existing connector patterns in the codebase

---

## Full Tech Spec

# Ppro — CreateClientAuthenticationToken Technical Specification

> Connector: Ppro
> Flow: CreateClientAuthenticationToken (MerchantAuthenticationService.CreateClientAuthenticationToken)
> Status: NOT CURRENTLY IMPLEMENTED — this spec defines the target design based on PPRO API patterns
> Generated: 2026-05-13

---

## 1. Connector Profile

| Property | Value |
|---|---|
| Connector name | Ppro |
| Display name | PPRO |
| Description | Global provider of local payment infrastructure (APMs, wallets, bank redirects) |
| Connector type | PaymentGateway |
| Primary flow scope | Authorize, Capture, Void, PSync, Refund, RSync, SetupMandate, RepeatPayment, CreateClientAuthenticationToken (target) |
| API family | REST / JSON |
| Production host | Configurable via `base_url` connector config field (e.g. `https://api.eu.ppro.com`) |
| Sandbox host | `https://api.sandbox.eu.ppro.com` |
| Regional hosts | `base_url` is merchant-configured — PPRO may issue per-region endpoints |
| API versioning | Path-based: `/v1/` prefix |
| Currency unit | Minor (integer) |

---

## 2. Authentication

| Property | Value |
|---|---|
| Scheme | HTTP Bearer Token + Merchant-ID header |
| Credentials required | `api_key` (bearer token), `merchant_id` (header), `base_url` (configurable) |

### Headers required for every request

```
Authorization: Bearer
Merchant-Id:
Content-Type: application/json
```

### Implementation notes

- The `api_key` is peeked from the masked `Secret` and injected as `Bearer ` into the `Authorization` header.
- The `merchant_id` is sent as its own `Merchant-Id` header (not part of the Bearer value).
- For mutating requests (POST), a `Request-Idempotency-Key` header is also required — populated from `connector_request_reference_id`.
- The `base_url` is dynamic (merchant-configured), not hardcoded in the connector — callers may point at sandbox or production.
- Connector config type: `ConnectorSpecificConfig::Ppro { api_key, merchant_id, base_url }`.

---

## 3. Supported Flows

| Flow | HTTP | Path | Notes |
|---|---|---|---|
| Authorize | POST | `/v1/payment-charges` | Creates a payment charge. Returns redirect URL in `authenticationMethods`. |
| Capture | POST | `/v1/payment-charges/{id}/captures` | Captures an authorized charge. Body: `{"amount": }`. |
| Void | POST | `/v1/payment-charges/{id}/voids` | Voids an authorized charge. Body: `{"amount": }`. |
| PSync | GET | `/v1/payment-charges/{id}` | Polls charge status. No body. |
| Refund | POST | `/v1/payment-charges/{id}/refunds` | Creates a refund. Body: `{"amount": , "refundReason": }`. |
| RSync | GET | `/v1/payment-charges/{id}` | Polls charge status to derive refund status from `refunds[]`. |
| SetupMandate | POST | `/v1/payment-agreements` | Creates a payment agreement (mandate). |
| RepeatPayment | POST | `/v1/payment-agreements/{agr_id}/payment-charges` | Charges against an existing agreement. |
| Webhooks | — | Inbound webhook (CloudEvents format) | PPRO pushes `PAYMENT_CHARGE_*` and `PAYMENT_AGREEMENT_*` events. |
| **CreateClientAuthenticationToken** | **POST** | **`/v1/client-sessions`** | **Target flow — not yet implemented in PPRO. See notes below.** |

### CreateClientAuthenticationToken — design notes

PPRO's current public API (`/v1/payment-charges`, `/v1/payment-agreements`) is entirely server-to-server. There is no dedicated client-session / client-token endpoint documented in the PPRO API today, which is why the field-probe result for this flow is `not_implemented`.

The intended design for this flow (once PPRO exposes such an endpoint) is:
- **Endpoint**: `POST /v1/client-sessions` (anticipated endpoint based on PPRO API versioning patterns)
- **Purpose**: Returns a short-lived session token that the merchant front-end can pass to PPRO's hosted checkout or SDK for client-side payment completion.
- **UCS domain mapping**: The response token is surfaced as `PaymentsResponseData::ConnectorSessionToken { session_token }`, consumed by the client via `MerchantAuthenticationService/CreateClientAuthenticationToken`.

If PPRO does not add a dedicated session endpoint, an alternative implementation strategy is to create a pre-authorization charge intent and surface its `id` as the session token (analogous to Nexinets' orderId approach).

---

## 4. Request Schema Highlights

### CreateClientAuthenticationToken Request

**Headers**
```
POST /v1/client-sessions HTTP/1.1
Authorization: Bearer
Merchant-Id:
Content-Type: application/json
Request-Idempotency-Key:
```

**Body** (proposed, modelled on `ClientAuthenticationTokenRequestData`)
```json
{
"amount": {
"value": 1000,
"currency": "EUR"
},
"paymentMethodType": "IDEAL",
"returnUrl": "https://merchant.example.com/return",
"consumer": {
"email": "customer@example.com",
"name": "Jane Doe"
}
}
```

| Field | Type | Required | Notes |
|---|---|---|---|
| `amount.value` | integer (minor) | yes | Mapped from `ClientAuthenticationTokenRequestData.amount` |
| `amount.currency` | string (ISO 4217) | yes | Mapped from `ClientAuthenticationTokenRequestData.currency` |
| `paymentMethodType` | string (SCREAMING_SNAKE_CASE enum) | no | Mapped from `ClientAuthenticationTokenRequestData.payment_method_type`. E.g. `IDEAL`, `BLIK`, `BANCONTACT`. |
| `returnUrl` | string (URL) | no | Return URL from `resource_common_data.return_url` |
| `consumer.email` | string | no | Customer email, from billing address or request |
| `consumer.name` | string | no | Customer full name |
| `webhooksUrl` | string | no | Webhook notification URL |

### Existing Authorize Request (reference for API shape)

```json
{
"paymentMethod": "IDEAL",
"paymentMedium": "ECOMMERCE",
"merchantPaymentChargeReference": "ref-001",
"amount": { "currency": "EUR", "value": 1000 },
"consumer": { "name": "Jane Doe", "email": "jane@example.com" },
"authenticationSettings": [
{ "type": "REDIRECT", "settings": { "returnUrl": "https://example.com/return" } }
],
"webhooksUrl": "https://example.com/webhook"
}
```

**Key enums for `paymentMethod`**: `IDEAL`, `BLIK`, `BANCONTACT`, `WECHATPAY`, `ALIPAY`, `MBWAY`, `SATISPAY`, `WERO`, `TRUSTLY`, `UPI`.

**Idempotency**: `Request-Idempotency-Key` header contains `connector_request_reference_id`. Resending the same key replays the response.

---

## 5. Response Schema Highlights

### CreateClientAuthenticationToken Response (proposed)

```json
{
"id": "sess_abc123",
"status": "ACTIVE",
"clientToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresAt": "2026-05-13T12:30:00Z",
"paymentMethodType": "IDEAL"
}
```

| Field | Type | Notes |
|---|---|---|
| `id` | string | Session identifier. Used as `ResponseId::ConnectorTransactionId`. |
| `status` | string | `ACTIVE` → success. Other values → failure. |
| `clientToken` | string | Opaque token surfaced to the front-end for SDK / hosted-checkout use. Mapped to `session_token` in `PaymentsResponseData`. |
| `expiresAt` | ISO8601 timestamp | Token TTL. Should be validated before use. |

### Existing Payment Charge Response (reference — PproPaymentsResponse)

```json
{
"id": "ch_abc123",
"status": "AUTHENTICATION_PENDING",
"amount": 1000,
"currency": "EUR",
"instrumentId": "inst_xyz",
"authenticationMethods": [
{
"type": "REDIRECT",
"details": {
"requestUrl": "https://checkout.provider.com/redirect?session=abc",
"requestMethod": "GET"
}
}
],
"failure": null
}
```

### Payment status → UCS AttemptStatus mapping

| PPRO status | UCS AttemptStatus |
|---|---|
| `AUTHORIZATION_PROCESSING` | `Pending` |
| `AUTHENTICATION_PENDING` | `AuthenticationPending` |
| `AUTHORIZATION_ASYNC` / `CAPTURE_PENDING` / `CAPTURE_PROCESSING` | `Authorized` |
| `CAPTURED` / `SUCCESS` | `Charged` |
| `FAILED` / `DISCARDED` / `REJECTED` / `DECLINED` | `Failure` |
| `VOIDED` | `Voided` |
| `REFUND_SETTLED` / `REFUNDED` | `Charged` (terminal) |

---

## 6. Error Handling

| HTTP | `status` (PproErrorResponse) | Cause |
|---|---|---|
| 400 | 400 | Bad request — malformed JSON, missing required field, invalid enum value |
| 401 | 401 | Unauthorized — missing or invalid `Authorization` Bearer token |
| 403 | 403 | Forbidden — valid token but insufficient permissions for this merchant |
| 404 | 404 | Not found — referenced resource (charge ID, agreement ID) does not exist |
| 409 | 409 | Conflict — idempotency key reused with different request body |
| 422 | 422 | Unprocessable entity — business rule violation (e.g. payment method not enabled for merchant) |
| 429 | 429 | Rate limited — too many requests; apply exponential backoff |
| 500 | 500 | Internal server error — transient PPRO-side failure |
| 503 | 503 | Service unavailable — PPRO system maintenance |

**Error response body** (`PproErrorResponse`):
```json
{
"status": 400,
"failureMessage": "Required field 'amount' is missing"
}
```

| Field | Type | Notes |
|---|---|---|
| `status` | u16 | Mirrors the HTTP status code |
| `failureMessage` | string | Human-readable error description |

**UCS ErrorResponse mapping**:
- `code` = `status.to_string()`
- `message` = `failureMessage`
- `reason` = None
- `attempt_status` = None
- `connector_transaction_id` = None

---

## 7. Webhooks / Async Notifications

### Delivery format

PPRO webhooks follow the **CloudEvents 1.0** specification:

```json
{
"specversion": "1.0",
"type": "PAYMENT_CHARGE_SUCCESS",
"source": "ppro-api",
"id": "evt_001",
"time": "2026-05-13T10:00:00Z",
"data": {
"charge": {
"id": "ch_abc123",
"status": "SUCCEEDED",
"amount": 1000,
"currency": "EUR"
}
}
}
```

### Subscription

Webhook URL is provided per-payment via the `webhooksUrl` field in the Authorize request body. There is no global webhook registration endpoint in the v1 API.

### Verification

- PPRO signs each webhook with `SHA-256(body + '.' + secret)` and delivers the hex digest in the `Webhook-Signature` header.
- UCS implementation: `PproWebhookSignature::verify_signature` reconstructs the digest from `body + '.' + secret` and compares hex-encoded bytes.

### Supported event types

| Event type | 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_BY_*` | `MandateRevoked` |

### Retry policy

PPRO retries failed webhook deliveries with exponential backoff. Merchants should respond `2xx` promptly to acknowledge receipt.

### Documented gaps

- `CreateClientAuthenticationToken` has no associated webhook event type — session expiry or token invalidation is not notified asynchronously.
- Dispute webhooks are not supported by PPRO (`process_dispute_webhook` returns `WebhooksNotImplemented`).
- Agreement webhooks (data variant `PproWebhookData::Agreement`) are not processed in `process_payment_webhook` or `process_refund_webhook` — only charge data is supported there.

---

## 8. References

| Source | Description |
|---|---|
| `crates/integrations/connector-integration/src/connectors/ppro.rs` | Main connector implementation (flows, auth, webhook logic) |
| `crates/integrations/connector-integration/src/connectors/ppro/transformers.rs` | Request/response type definitions and TryFrom impls |
| `crates/types-traits/domain_types/src/connector_types.rs` (line 1603) | `ClientAuthenticationTokenRequestData` struct definition |
| `docs-generated/connectors/ppro.md` | Auto-generated SDK API reference for Ppro flows |
| `data/field_probe/ppro.json` | Field probe output — `create_client_authentication_token` status: `not_implemented` |
| `crates/internal/integration-tests/src/connector_specs/ppro/specs.json` | Supported integration test suites (does not yet include CreateClientAuthenticationToken) |
| `crates/integrations/connector-integration/src/connectors/nexinets/transformers.rs` (line 940) | Reference implementation of `ClientAuthenticationToken` via orderId pattern |
| PPRO REST API v1 (sandbox: `https://api.sandbox.eu.ppro.com`) | Authoritative API reference |

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.