Webhooks (Draft)
Using the webhook, merchant application can get real time updates on the payment status and payment-method related events.
Payments
Supported Events
Event Type | Description | Applicable payment method types |
---|---|---|
PAYMENT_SUCCEEDED | Emitted when payment is captured and is successful. | CARD, BANK_ACCOUNT |
PAYMENT_SUCCEDED | Emitted when payment is captured and is successful. **Will be deprecated** | CARD, BANK_ACCOUNT |
PAYMENT_FAILED | Emitted when a payment fails. | CARD, BANK_ACCOUNT |
PAYMENT_ACCEPTED | Emitted when a payment is submitted successfully and is being processed. | CARD, BANK_ACCOUNT |
PAYMENT_AUTHORIZED | Emitted when a payment is AUTHORIZED. Authorized payment should be captured later using capture endpoint to complete the payment. | CARD |
PAYMENT_CANCELED | Emitted when a payment is canceled. | CARD, BANK_ACCOUNT |
Event Structure
Field | Type | Valid values | Description |
---|---|---|---|
name | Enum | PAYMENT_SUCCEEDED, PAYMENT_FAILED, PAYMENT_ACCEPTED, PAYMENT_AUTHORIZED, PAYMENT_CANCELED | Event name |
payload | Payload | Event Payload |
Payload Structure
Field | Type | Valid values | Description |
---|---|---|---|
amount | long | 50 - 99999999 | Amount (in cents) requested for payment |
authorizedAmount | long | 50 - 99999999 | (PRE_AUTH) Maximum amount approved for capture (in cents); Starting R27, pre-authorization and partial-authorization, should infer this field to consider authorized amount; Prior to R27, infer amount field; |
capturedAmount | long | 50 - 99999999 | (SALE) Amount(in cents) immediately settled. (PRE_AUTH) Amount(in cents) successfully captured. |
partialAuthorization | boolean | true/false | (PRE_AUTH) Indicates whether a partial authorization was requested. |
description | string | Max length 50 | Payment Description |
id | uuid | valid uuid4 | Payment Identifier |
merchantId | uuid | valid uuid4 | Merchant Identifier |
merchantTransactionId | string | Max length 50 | MerchantTransactionId sent by the merchant with original payment request |
paymentDateUtc | date | Date in ISO string format | Payment completed datetime |
paymentMethod | PaymentMethod | Paymentmethod Description | |
error | Error | Error Information | |
consent | Consent | Consent required for bank account payments |
Error Structure
Field | Type | Valid values | Description |
---|---|---|---|
code | string | Max length 50 | Short code for error |
description | string | Max length 255 | Error description |
errorDetails | ErrorDetails | Error details |
Error Details Structure
Field | Type | Valid values | Description |
---|---|---|---|
code | string | Max length 100 | code for error |
message | string | Max length 255 | Error description |
declineCode | string | Max length 100 | Decline Code |
networkAdviceCode | string | Max length 100 | Network Advice Code |
networkDeclineCode | string | Max length 100 | Network Decline Code |
Important Note
In the event that payment is captured and succeeded, we will be publishing two events: PAYMENT_SUCCEDED and PAYMENT_SUCCEEDED.
PAYMENT_SUCCEDED will be deprecated by 06/15/2024 and only PAYMENT_SUCCEEDED will be published going forward.
Sample Event
Events will be sent in JSON format.
card object under paymentMethod is deprecated in favor of paymentMethodDetails. Please refer PaymentMethod for more details.
{
"name": "PAYMENT_SUCCEEDED"
"payload": {
"amount": 1500,
"description": "Payment Description",
"id": "6ab9bf74-03e0-4f47-bd70-bf57b103a5fd",
"merchantId": "44387763-4eeb-4592-a564-b10aadee95be",
"merchantTransactionId": "e31de58d-cb20-40ff-ad58-b99d500z0001",
"paymentDateUtc": "2011-10-05T14:48:00.000Z",
"paymentMethod": {
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"card": {
"last4": "string",
"type": "CARD",
"status": "ACTIVE",
"cardBrand": "VISA",
"expiryYear": 0,
"nameOnCard": "string",
"expiryMonth": 0,
"zipCode": "string",
"manufacturerCard": false
},
"paymentMethodDetails": {
"last4": "string",
"type": "CARD",
"status": "ACTIVE",
"cardBrand": "VISA",
"expiryYear": 0,
"nameOnCard": "string",
"expiryMonth": 0,
"zipCode": "string",
"manufacturerCard": false
},
"default": true,
"paymentMethodType": "CARD",
"nickname": "string"
},
"error": {
"code": "card_declined",
"message": "Your card has insufficient funds.",
"declineCode": "generic_decline",
"errorDetails": {
"code": "no_account",
"message": "The customer's bank account could not be located.",
"declineCode": "generic_decline",
"networkDeclineCode": "51",
"networkAdviceCode":"02"
}
}
}
}
Payment-method
Supported Events
- PAYMENT_METHOD_CREATED - Emitted when a payment method is created.
- PAYMENT_METHOD_UPDATED - Emitted when a payment method is updated.
- PAYMENT_METHOD_DELETED - Emitted when a payment method is deleted.
- PAYMENT_METHOD_REPLACED - Emitted when a payment method is replaced, find more details here.
Event Structure
Field | Type | Valid values | Description |
---|---|---|---|
name | Enum | PAYMENT_METHOD_CREATED, PAYMENT_METHOD_UPDATED, PAYMENT_METHOD_DELETED, PAYMENT_METHOD_REPLACED | Event name |
payload | Payload | Event Payload |
Payload Structure
Field | Type | Valid values | Description |
---|---|---|---|
paymentMethod | PaymentMethod | Paymentmethod Description | |
customer | Customer | Customer Description | |
agent | Agent | Agent Description | |
deletedPaymentMethodId | UUID | Valid UUID | Deleted PaymentMethod Id, in PAYMENT_METHOD_REPLACED event |
Customer Structure
Field | Type | Valid values | Description |
---|---|---|---|
enterpriseId | string | Max length | Payment method Id |
hsid | uuid | valid uuid4 | Healthsafe Identifier |
metadata | string | Max length 50 | Client provided additional metadata |
Agent Structure
Field | Type | Valid values | Description |
---|---|---|---|
firstName | string | Max length 50 | First name of Agent |
lastName | string | Max length 50 | First name of Agent |
userId | string | Max length 50 | MSId of Agent |
isAccessVerified | boolean | true/false | Is access verified by merchant |
Paymentmethod Structure
Field | Type | Valid values | Description |
---|---|---|---|
id | uuid | valid uuid4 | Payment method Id |
Card | Card Description when payment Method is of type CARD. Deprecated in favour of paymentMethodDetails | ||
nickname | string | Max length 50 | Payment method nickname |
default | boolean | true/false | determines if the payment method is default for the customer |
paymentMethodType | string | Max length 50 | Payment method type can be CARD or BANK_ACCOUNT |
paymentMethodDetails | Card or ACH | One of Card or ACH |
Card Structure
Field | Type | Valid values | Description |
---|---|---|---|
nameOnCard | string | Max length 50 | Name of the customer |
cardBrand | string | VISA, AMEX, DINERS, DISCOVER, JCB, MASTERCARD, UNIONPAY, UNKNOWN | Card brand |
expiryMonth | long | 01-12 | Month of expiration |
expiryYear | long | Max length 4 | Year of expiration |
last4 | string | Max length 4 | Last four digits of the card |
zipCode | string | Max length 5 | 5 digit zipcode |
status | string | ACTIVE/EXPIRED | Status of the card |
manufacturerCard | boolean | true/false | Determines if the card is manufacturer card or not. Only Agents can flag certain cards as manufacturer cards. Cards flagged as manufacturer cards cannot be default card. |
ACH Structure
Field | Type | Valid values | Description |
---|---|---|---|
type | string | BANK_ACCOUNT | Type of the PaymentMethod |
accountHolderType | string | individual or company | Account holder type |
accountType | string | checking or savings | Account Type |
bankName | string | Bank Name | |
last4 | string | Last 4 digits of bank account number | |
routingNumber | string | Routing number of bank | |
nameOnAccount | string | Name on Account | |
status | enum | ACTIVE and INVALIDATED | Bank Account Status |
Consent Structure
Field | Type | Valid values | Description |
---|---|---|---|
merchantConsentId | string | Valid UUID | Consent Id |
merchantConsentText | string | Consent text | |
collectionTimestamp | string | Timestamp of when the consent was collected | |
collectionDetails | ConsentCollectionDetails | Details about how consent was collected |
Consent Collection Details Structure
Field | Type | Valid values | Description |
---|---|---|---|
type | enum | WEB, TEL, PPD | Consent type |
web | ConsentCollectionWeb | Details of consent collected through the website | |
tel | ConsentCollectionTel | Details of consent collected through the telephone |
Consent Collection Web Structure
Field | Type | Valid values | Description |
---|---|---|---|
ipAddress | string | Valid IP address | IP address from which the consent is collected |
userAgent | string | Agent involved in the consent collection |
Consent Collection Tel Structure
Field | Type | Valid values | Description |
---|---|---|---|
phoneNumber | PhoneNumber | Valid country code and phone number | Phone number from which the consent is collected |
Phone Number Structure
Field | Type | Valid values | Description |
---|---|---|---|
countryCode | string | Valid country code | Country code |
number | string | Valid phone number | Phone number |
Sample Event
Events will be sent in JSON format.
CARD Payment Method
card object under paymentMethod is deprecated in favor of paymentMethodDetails. Please refer PaymentMethod for more details.
{
"name": "PAYMENT_METHOD_CREATED | PAYMENT_METHOD_UPDATED | PAYMENT_METHOD_DELETED | PAYMENT_METHOD_REPLACED",
"payload": {
"paymentMethod": {
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"card": {
"last4": "string",
"type" : "CARD",
"status": "ACTIVE",
"cardBrand": "VISA",
"expiryYear": 0,
"nameOnCard": "string",
"expiryMonth": 0,
"zipCode": "string",
"manufacturerCard": false
},
"paymentMethodDetails": {
"last4": "string",
"type" : "CARD",
"status": "ACTIVE",
"cardBrand": "VISA",
"expiryYear": 0,
"nameOnCard": "string",
"expiryMonth": 0,
"zipCode": "string",
"manufacturerCard": false
},
"default": true,
"paymentMethodType": "CARD",
"nickname": "string"
},
"customer": {
"enterpriseId": "enterprise id",
"hsid": "hsid",
"dateOfBirth": "dob",
"metadata": {
"patientId": "rx-patient-id"
}
},
"agent": {
"firstName": "First Name",
"lastName": "Last Name",
"userId": "msId",
"isAccessVerified": true
},
"deletedPaymentMethodId": "597f6eca-6276-4993-bfeb-53cbbbba6f12"
}
}
Payment with BANK ACCOUNT
{
"name": "PAYMENT_SUCCEDED",
"payload": {
"amount": 5000,
"capturedAmount": 5000,
"partialAuthorization": false,
"description": "Test Payment from Postman_003",
"id": "27f986f9-8440-4d30-8816-b3faf82dfd2e",
"merchantId": "44387763-4eeb-4592-a564-b10aadee95be",
"merchantTransactionId": "a812eb9d-9726-4764-b30a-06c234a75fa1",
"paymentDateUtc": "2024-05-06T12:26:27.192037",
"consent": {
"merchantConsentText": "some text",
"collectionTimestamp": "1054684654",
"collectionDetails": {
"type": "TEL",
"tel": {
"inboundPhoneNumber": {
"countryCode": "1",
"number": "1234567890"
}
}
}
},
"paymentMethod": {
"paymentMethodType": "BANK_ACCOUNT",
"nickname": "Nickname test",
"paymentMethodDetails": {
"type": "BANK_ACCOUNT",
"accountHolderType": "individual",
"accountType": "checking",
"bankName": "STRIPE TEST BANK",
"last4": "6789",
"routingNumber": "110000000",
"nameOnAccount": "Name on account test",
"status": "ACTIVE"
},
"default": true
}
}
}
Partial authorization
{
"name": "PAYMENT_AUTHORIZED",
"payload": {
"amount": 5000,
"capturedAmount": 0,
"authorizedAmount": 3500,
"partialAuthorization": true,
"description": "Test Payment from Postman_16",
"id": "2b12ce09-73eb-43c3-b39b-89419f319746",
"merchantId": "44387763-4eeb-4592-a564-b10aadee95be",
"merchantTransactionId": "100ee626-0795-4b5e-b0d9-c543868207b1",
"paymentDateUtc": "2024-05-06T12:43:19.550101",
"paymentMethod": {
"card": {
"cardBrand": "VISA",
"expiryMonth": 5,
"expiryYear": 2055,
"last4": "0014",
"status": "ACTIVE",
"manufacturerCard": false
},
"paymentMethodType": "CARD",
"paymentMethodDetails": {
"type": "CARD",
"cardBrand": "VISA",
"expiryMonth": 5,
"expiryYear": 2055,
"last4": "0014",
"status": "ACTIVE",
"manufacturerCard": false
},
"default": false
}
}
}
REFUND
Refund events are sent when a refund is processed. Refunds can be processed for both CARD and BANK_ACCOUNT payment methods. There are two types of refunds that can be processed i.e., linked
and unlinked
refunds.
- Linked refunds are processed against a payment and unlinked refunds are processed without a payment.
- In case of unlinked refunds i.e., credit / cashback, payment method id is needed to process the refund.
Supported Events
Event Type | Description | Applicable payment method types |
---|---|---|
REFUND_SUCCESS | Emitted when refund is successful. | CARD, BANK_ACCOUNT |
REFUND_FAILED | Emitted when a refund fails. | CARD, BANK_ACCOUNT |
REFUND_PENDING | Emitted when a refund status is pending. | CARD, BANK_ACCOUNT |
Event Structure
Field | Type | Valid values | Description |
---|---|---|---|
name | Enum | REFUND_SUCCESS, REFUND_FAILED, REFUND_PENDING | Event name |
payload | Payload | Event Payload |
Payload Structure
Field | Type | Valid values | Description |
---|---|---|---|
amount | long | 50 - 99999999 | Refund amount in United States Cents |
reason | String | Max length 50 | Refund reason |
refundId | uuid | valid uuid4 | Refund Identifier |
Payment | Payment | Payment Details | |
merchantTransactionId | String | Max length 50 | MerchantTransactionId sent by the merchant with original refund request |
merchantId | uuid | valid uuid4 | Merchant Identifier |
metadata | String | Max length 50 | Client provided additional metadate |
paymentMethod | PaymentMethod | Paymentmethod Description | |
error | Error | Error Information | |
status | String | COMPLETED and FAILED | Refund Status |
Error Structure
Field | Type | Valid values | Description |
---|---|---|---|
code | string | Max length 50 | Short code for error |
description | string | Max length 255 | Error description |
errorDetails | ErrorDetails | Error details |
Error Details Structure
Field | Type | Valid values | Description |
---|---|---|---|
code | string | Max length 100 | code for error |
message | string | Max length 255 | Error description |
declineCode | string | Max length 100 | Decline Code |
networkAdviceCode | string | Max length 100 | Network Advice Code |
networkDeclineCode | string | Max length 100 | Network Decline Code |
Sample Event
Events will be sent in JSON format.
{
"name": "REFUND_SUCCESS | REFUND_FAILED | REFUND_PENDING",
"payload": {
"amount": 100,
"reason": "DUPLICATE",
"status": "COMPLETED",
"payment": {
"id": "d3398a06-e038-4aaa-9a6f-08e6884b6aa9",
"amount": 900000,
"merchantId": "b955db5e-aef2-47de-bbb9-c80b9cc16e8f",
"description": "UI widget payment",
"capturedAmount": 900000,
"partialAuthorization": false,
"merchantTransactionId": "68da00a3-d96c-4731-ad9c-f7b4f005b04c"
},
"refundId": "242ecd9b-333a-4537-ba95-bea1de6ce973",
"merchantId": "b955db5e-aef2-47de-bbb9-c80b9cc16e8f",
"paymentMethod": {
"card": {
"last4": "4444",
"status": "ACTIVE",
"zipCode": "94043",
"cardBrand": "MASTERCARD",
"expiryYear": 2026,
"nameOnCard": "Card Holder Name",
"expiryMonth": 12,
"manufacturerCard": false
},
"default": false,
"nickname": "",
"paymentMethodType": "CARD",
"paymentMethodDetails": {
"type": "CARD",
"last4": "4444",
"status": "ACTIVE",
"zipCode": "94043",
"cardBrand": "MASTERCARD",
"expiryYear": 2026,
"nameOnCard": "Card Holder Name",
"expiryMonth": 12,
"manufacturerCard": false
}
},
"merchantTransactionId": "b6d52a1f-b4e7-4de7-85de-9bd5032d7643"
},
"error": {
"code": "VENDOR_ERROR",
"message": "Cannot issue refund on expired or cancelled card",
"declineCode": "generic_decline",
"errorDetails": null
}
}
Merchant Webhook Implemetation Requirements
- Merchant should expose a https endpoint that should be reachable in public internet.
- Merchant should validate the request using the Authorization token that comes as part of the request header. The authorization token is a JWT token. A public key for validating the token will be provided as a part of Merchant onboarding by CCG team.
Validating the JWT Authorization Token
The JWT token will be sent as Authorization Header in the HTTPS request.
# Header
Authorization:Bearer eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ7bWVyY2hhbnRJZH0iLCJpYXQiOjE1MTYyMzkwMjIsImV4cCI6MTUxNjI0OTAyMn0.188mvtjbW1xf6fCFNun0ZjlreckxFnUEEGZO-rvvdp3II-70C-M_W7QP6Dm7B57qnZJq6lPWxCjJnbF3hkTtAg
The JWT token should be validated for
- Expiry
- Signature
The JWT tokens will be signed using ES256 (Eliptical Curve Cryptography with 256 bit hashing) algorithm.
Failures and Retries
Retry attempts will happen when we received a failure from the webhook endpoint. The below table has the list of errors which are not retriable.
Endpoint | Not Retriable Errorcodes |
---|---|
Webhook | 400 Bad Request, 413 Request Entity Too Large, 403 Forbidden, 404 Not Found, 401 Unauthorized |
If the error returned from webhook is not among the above list we will perform retry. We wait 30 seconds for a response after delivering a message. After 30 seconds, if the endpoint hasn’t responded, the message is queued for retry. We are using an exponential backoff retry policy for event delivery. we perform 5 attempts in 50 mins with exponential backoff.