Skip to main content

Embedded Experience

Setup​

Generate a Session​

  • From the Merchant's backend, make a call to /POST sessions to generate session and pass it to the web app.
  • Widget's capabilities can be customized using config property in session 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 global window object

To reduce the load time for widget refer to

Placeholder values

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​

OptionValuesDescription
rootElement
required
Element
Valid DOM nodeCCG Widget will be injected into this DOM node
checkoutSessionId
required
string
-Valid checkout session id
appEnv
required
string
stage prodWidget's environment variable
appearance
Appearance
default: Optum ThemeBrand/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' }
onSuccess
Function
param type
(data) => voidCalled when payment is successful
onError
Function
param type
(data) => voidCalled when there is an error with the checkout session or a payment failure
onEvent
Function
param type
(data) => voidCalled 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
    ccgWidget = OptumCCGWidgetInitializer(options); // options includes the new checkout session id
    and then
    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​

OptionValuesDescription
type
modal, inline, drawer
modal, inline, drawerEmbedded 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​

PropertyValuesDescription
type
modal
modal

Auto Scroll​

PropertyValuesDescription
autoScrollEnabledtrue, falsedefaults to false

Container Config Examples​

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​

PropertyValues
codePAYMENT_SUCCEEDED
description
string
payload
CheckoutSession details

onError Data​

PropertyTypeValues
code
deprecated in favor of title
stringINITIALIZATION_ERROR
SESSION_TIMEOUT
SESSION_TIMEOUT
SESSION_ALREADY_PROCESSED
INVALID_REQUEST
PAYMENT_METHOD_ERROR
description
deprecated in favor of description
string
payload (optional)CheckoutSession
titlestringINITIALIZATION_ERROR
SESSION_TIMEOUT
SESSION_TIMEOUT
SESSION_ALREADY_PROCESSED
INVALID_REQUEST
PAYMENT_METHOD_ERROR
detailstring
status (optional)number
errorDetails
supported starting v2.10.0
populated only for payment related error
null or ErrorDetails

onEvent Data​

PropertyValues
codeWIDGET_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​

Alternative Setup - Module (npm/yarn)​

Install the package​

Package Availability

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>