Embedded Experience
Setupβ
Generate a Sessionβ
- From the Merchant's backend, make a call to
/POST sessions
to generatesession
and pass it to the web app. - Widget's capabilities can be customized using
config
property insession
request object - Refer Generate a Session for details
/POST session (request/response)
## /POST sessions request
curl --location --request POST 'https://api.uhg.com/api/financial/commerce/nonprodcheckout/v1/sessions' \
--header 'X-Merchant-Id: <x-merchant-id>' \
--header 'X-Upstream-Env: <x-upstream-env>' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <authorization-token>' \
--data-raw '{
"customer": {
"firstName": "foo",
"lastName": "bar",
"email": "foo.bar@email.com",
"ssnLastFour": "1234",
"dateOfBirth": "1970-31-12",
"phoneNumber": {
"number": "9876543210",
"countryCode": "1"
},
"zip5": "54321",
"hsid": "120c5730-e796-4448-8da9-081fde4e3e79",
"metadata": {}
},
"payment": {
"merchantTransactionId": "f32736c8-266a-4da1-af16-293fa02a351a",
"amount": 1200,
"authorizeCard": false,
},
"config": {
"modes": [
"PAYMENT_METHOD_ENTRY"
]
}
}'
// /POST sessions reponse
{
"url": "/sessions/<CHECKOUT_SESSION_ID>",
"data": {
"sessionId": "<CHECKOUT_SESSION_ID>",
"hostedUrl": "<CCG_DOMAIN>/checkout-sessions/<CHECKOUT_SESSION_ID>"
}
}
Load the widgetβ
<script src="https://<DOMAIN>.healthsafepay.com/wallet/<VERSION>/<FILE_NAME>.js"></script>
- Integrate by adding
<script>
tag.optumCCG
object is added to the globalwindow
object
To reduce the load time for widget refer to
DOMAIN: walletstage
| walletprod
VERSION: v2 see Version History for more details
FILE_NAME: ccg-widget.js
| ccg-widget.min.js
Example:
https://walletstage.healthsafepay.com/wallet/v2/ccg-widget.min.js
https://walletstage.healthsafepay.com/wallet/v2/ccg-widget.js
Access the widget initializerβ
const OptumCCGWidgetInitializer = window.optumCCG.OptumCCGWidgetInitializer;
Initializeβ
const options = {...};
const ccgWidget = OptumCCGWidgetInitializer(options);
OptumCCGWidgetInitializer
function is used to setup and manage the widget. This sets up the widget, attaches to DOM and returns the instance
Renderβ
ccgWidget.render();
Makes the embedded payment modal window visible on screen. By default, the widget is not visible when attached to DOM
Full Exampleβ
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CCG Widget Integration</title>
</head>
<body>
<h1>CCG Widget Integration</h1>
<button id="payRemainingBalance">Pay Remaining Balance</button>
<div id="ccg-widget-container"></div>
<!-- 1. Load the widget -->
<script src="https://walletprod.healthsafepay.com/wallet/v2/ccg-widget.min.js"></script>
<script>
let ccgWidget;
document
.querySelector("#payRemainingBalance")
.addEventListener("click", () => {
// 2. Initialize widget once
if (!ccgWiget) {
// 3. Access the widget initializer
const OptumCCGWidgetInitializer =
window.optumCCG.OptumCCGWidgetInitializer;
const options = {
rootElement: document.querySelector("#ccg-widget-container"),
checkoutSessionId: "REPLACE_WITH_CHECKOUT_SESSION_ID",
appEnv: "prod",
onSuccess: (data) => {},
onError: (data) => {},
onEvent: (data) => {},
};
// 4. Initialize
ccgWidget = OptumCCGWidgetInitializer(options);
}
// 5. Render
ccgWidget.render();
});
</script>
</body>
</html>
Removing the widgetβ
ccgWidget.unmount();
ccgWidget = null;
This unmounts the widget from the DOM
Additional Detailsβ
OptumCCGWidgetInitializer method optionsβ
Option | Values | Description |
---|---|---|
rootElement required Element | Valid DOM node | CCG Widget will be injected into this DOM node |
checkoutSessionId required string | - | Valid checkout session id |
appEnv required string | stage prod | Widget's environment variable |
appearanceAppearance | default: Optum Theme | Brand/styling configuration see Theming and Styling deprecated >= 2.1.0 Refer /POST sessions on how to pass appearance as part of session creation |
containerConfig object Container Config | default: { type: 'modal' } | |
onSuccessFunction param type | (data) => void | Called when payment is successful |
onErrorFunction param type | (data) => void | Called when there is an error with the checkout session or a payment failure |
onEventFunction param type | (data) => void | Called for all other events (i.e., widget loaded , open , closed , PAYMENT_METHOD_SELECTION ) |
OptumCCGWidgetInitializer instanceβ
render Function
β
- show the widget as a modal
- this function would probably be called from your button's onClick handler
unmount Function
β
- unmount widget from host application
Widget unmounting and re-initializationβ
Following scenarios will require the widget to be unmounted and reinitialized
- When
onError
is called - When
onSuccess
is called
To use the widget again you must:
- call unmount on the old instance
ccgWidget.unmount();
- initialize the widget again
and then
ccgWidget = OptumCCGWidgetInitializer(options); // options includes the new checkout session id
ccgWidget.render();
Guest vs Wallet Experienceβ
Refer Guest vs Wallet Experience
Managing 'MerchantTransactionId'β
Refer Managing MerchantTransactionId
Data Modelsβ
Callback dataβ
{
code: string;
description?: string;
payload?: unknown;
}
Container Configβ
Option | Values | Description |
---|---|---|
typemodal , inline , drawer | modal , inline , drawer | Embedded experience variant defaults to modal |
secondaryContainer Secondary Container | An optional configuration for a inline modal container | |
autoScrollEnabled Auto Scroll | An optional configuration only for inline container, defaults to false |
Secondary Containerβ
Property | Values | Description |
---|---|---|
typemodal | modal |
Auto Scrollβ
Property | Values | Description |
---|---|---|
autoScrollEnabled | true , false | defaults to false |
Container Config Examplesβ
Modalβ
const modalConfig: ContainerConfig = {
type: "modal",
};
Inlineβ
const inlineConfig: ContainerConfig = {
type: "inline",
};
const inlineWithSecondaryConfig: ContainerConfig = {
type: "inline",
secondaryContainer: {
type: "modal",
},
};
Drawerβ
const drawerConfig: ContainerConfig = {
type: "drawer",
};
Inline with Auto Scrollβ
const inlineConfig: ContainerConfig = {
type: "inline",
autoScrollEnabled: true,
};
onSuccess Dataβ
Property | Values |
---|---|
code | PAYMENT_SUCCEEDED |
descriptionstring | |
payload CheckoutSession details |
onError Dataβ
Property | Type | Values |
---|---|---|
code deprecated in favor of title | string | INITIALIZATION_ERROR SESSION_TIMEOUT SESSION_TIMEOUT SESSION_ALREADY_PROCESSED INVALID_REQUEST PAYMENT_METHOD_ERROR |
description deprecated in favor of description | string | |
payload (optional) | CheckoutSession | |
title | string | INITIALIZATION_ERROR SESSION_TIMEOUT SESSION_TIMEOUT SESSION_ALREADY_PROCESSED INVALID_REQUEST PAYMENT_METHOD_ERROR |
detail | string | |
status (optional) | number | |
errorDetails supported starting v2.10.0 populated only for payment related error | null or ErrorDetails |
onEvent Dataβ
Property | Values |
---|---|
code | WIDGET_LOADED WIDGET_OPEN WIDGET_CLOSED PAYMENT_METHOD_SELECTION |
description (optional) string |
CheckoutSessionβ
{
checkoutSession: {
id: string;
customerId: string;
paymentId: string;
checkoutSessionStatus: 'COMPLETED' | 'FAILED';
merchantId: string;
vendorMerchantId: string;
checkoutRequest: {
statementDescriptorSuffix: string;
amount: number;
merchantTransactionId: string;
authorizeCard: boolean; // true = PRE_AUTH; false = SALE;
paymentType: string; // DEPRECATED; possible values: SALE, PRE_AUTH;
metadata: {
[key: string]: string | undefined;
};
};
error: null; // backward compatability only; For error details, inspect 'onError' callbacks
};
}
Eventsβ
onError Scenariosβ
Network Error
{
"code": "INITIALIZATION_ERROR",
"description": "Network Error"
}
Attempt to initialize CCG Widget with empty "checkoutSessionId"
{
code: "INVALID_SESSION",
description: "session id is invalid",
}
Attempt to initialize CCG Widget with an invalid value for "checkoutSessionId" (eg: "a12u", doesn't conform to UUID format)
{
code: "INVALID_REQUEST",
description: "Please review API specs https://docs.healthsafepay.com/api-reference/",
}
Attempt to initialize CCG Widget with expired "checkoutSessionId"
{
code: "SESSION_TIMEOUT",
description: "session no longer valid",
}
Attempt to initialize CCG Widget with canceled "checkoutSessionId"
{
code: "SESSION_CANCELED",
description: "Your session is no longer valid",
}
Attempt to initialize CCG Widget with "checkoutSessionId" that's already processed (succeeded/failed)
{
code: "SESSION_ALREADY_PROCESSED",
description: "session is already processed",
}
Payment Failure
{
"code": "PAYMENT_METHOD_ERROR",
"description": "Your card has insufficient funds.",
"payload": {
"checkoutSession": {
"checkoutRequest": {
"merchantTransactionId": "xxx-xxx-xxx-xxx-xxx",
"amount": 1500
},
"paymentId": "xxx-xxx-xxx-xxx-xxx"
}
},
"detail": "Your card has insufficient funds.",
"status": 405,
"title": "PAYMENT_METHOD_ERROR"
}
onEvent Scenariosβ
Widget loaded
{
code: "WIDGET_LOADED";
}
Widget open
{
code: "WIDGET_OPEN";
}
Widget closed
{
code: "WIDGET_CLOSED";
}
Payment Method Selection
{
code: "PAYMENT_METHOD_SELECTION";
}
onSuccess Scenariosβ
Payment Success
{
"code": "PAYMENT_SUCCEEDED",
"description": "Payment was successfully processed",
"payload": {
"checkoutSession": {
"id": "xxx-xxx-xxx-xxx-xxx",
"checkoutRequest": {
"paymentType": "SALE",
"authorizeCard": false,
"merchantTransactionId": "xxx-xxx-xxx-xxx-xxx",
"amount": 10012,
"statementDescriptorSuffix": null
}
}
},
"data": {
"sessionId": "xxx-xxx-xxx-xxx-xxx",
"status": "COMPLETED",
"payment": {
"id": "xxx-xxx-xxx-xxx-xxx",
"amount": 10012,
"description": "xxx-xxx-xxx-xxx-xxx",
"merchantTransactionId": "xxx-xxx-xxx-xxx-xxx",
"merchantId": "xxx-xxx-xxx-xxx-xxx",
"paymentType": "SALE",
"currencyCode": "usd",
"status": "COMPLETED",
"paymentMethodId": "xxx-xxx-xxx-xxx-xxx",
"metadata": {
"checkoutId": "xxx-xxx-xxx-xxx-xxx",
"merchantId": "xxx-xxx-xxx-xxx-xxx",
"referenceId": "xxx-xxx-xxx-xxx-xxx",
"merchantName": "xxx-xxx-xxx-xxx-xxx",
"ccg_nameoncard": "xxx-xxx-xxx-xxx-xxx",
"merchantGroupId": "xxx-xxx-xxx-xxx-xxx",
"merchantTransactionId": "xxx-xxx-xxx-xxx-xxx",
"ccg_paymentmethodnickname": "xxx-xxx-xxx-xxx-xxx"
},
"error": null,
"statementDescriptorSuffix": null,
"paymentDetails": {
"healthcare": {
"iias": {
"qualifiedAmount": 700,
"qualifiedAmountDetails": {
"prescriptionAmount": 1200
}
},
"visionAmount": 25
}
},
"paymentMethod": null
}
}
}
Theming and Stylingβ
- Refer Theming and Styling
Alternative Setup - Module (npm/yarn)β
Install the packageβ
This package is only available in private Optum NPM repo (repo1).
yarn add @optum-ccg/convenient-checkout-ui --save
or
npm install @optum-ccg/convenient-checkout-ui --save
Load the widgetβ
import { OptumCCGWidgetInitializer } from "@optum-ccg/convenient-checkout-ui/dist/widget/ccg-widget.min";
Initializeβ
const options = {
rootElement: document.querySelector("#ccg-widget-container"),
checkoutSessionId: "REPLACE_WITH_CHECKOUT_SESSION_ID",
appEnv: "prod",
};
// 3. Initialize
const ccgWidget = OptumCCGWidgetInitializer(options);
Renderβ
ccgWidget.render();
Full Example (npm)β
import React, { useRef, useState } from "react";
/* 1. include ccg widget as COMPILE-TIME dependency */
import { OptumCCGWidgetInitializer } from "@optum-ccg/convenient-checkout-ui/dist/widget/ccg-widget.min";
const PaymentWidget = ({
appEnv,
checkoutSessionId,
containerType,
onSuccess,
onError,
onEvent,
}) => {
/* 4. CCG widget type (required) */
let ccgWidget: ReturnType<typeof OptumCCGWidgetInitializer>;
const widgetContainer = useRef(null);
useEffect(() => {
if (widgetContainer.current) {
// 5. Initialize
ccgWidget = OptumCCGWidgetInitializer({
rootElement: widgetContainer.current,
checkoutSessionId,
appEnv,
containerConfig: {
type: containerType,
},
onSuccess,
onError,
onEvent,
});
// 6. display
ccgWidget.render();
}
}, []);
return <div ref={widgetContainer}></div>;
};
export default PaymentWidget;
Improve Page Load Time with cdnβ
To Improve the page load time use "async" and "defer" which significantly reduces the "DOMContentloadtime" which doesn't block the page and makes it available for users while the script is being loaded in background.
Code Snippet for using async and deferβ
document.getElementById("ccgScript").addEventListener("load", (event) => {
initWidget();
renderWidget();
});
where βccgScriptβ is the id for <script > tag for ccg-widget.min.js.
<script id="ccgScript" src="https://walletprod.healthsafepay.com/wallet/v2/ccg-widget.min.js" async defer></script>