@healthcloudai/hc-stripe-connector
v0.2.0
Published
Healthcheck Stripe Payment SDK with TypeScript
Maintainers
Readme
Stripe Connector
This connector handles authenticated Stripe PaymentIntent payment flows for the active patient session.
HCStripeClient uses the active authenticated session from HCLoginClient, so payment requests do not require a separate authentication setup.
The backend creates the Stripe PaymentIntent and the UI completes the payment using Stripe SDK, PaymentSheet, or Payment Element.
Node.js Package
@healthcloudai/hc-stripe-connectorClient
HCStripeClientAuthentication
Reuses the authenticated patient session from HCLoginClient.
Features
- Create a backend-owned Stripe PaymentIntent for the authenticated patient
- Return the tenant-level Stripe publishable key to the UI
- Return the PaymentIntent client secret to the UI
- Complete or verify the PaymentIntent status after UI confirmation
- Reuse the authenticated login client for headers and base URL
- Optionally attach API key headers to Stripe connector requests
Setup
Configure and authenticate HCLoginClient before passing it into HCStripeClient.
Reuse the same authenticated HCLoginClient instance so both connectors share the same authentication state.
const httpClient = new FetchClient();
const authClient = new HCLoginClient(
httpClient
);
authClient.configure(
"healthcheck",
"dev"
);
await authClient.login(
"<PATIENT_EMAIL>",
"ExamplePassword123!"
);
const stripeClient =
new HCStripeClient(
httpClient,
authClient
);API Key
Use setApiKey(...) to attach an API key header to Stripe connector requests.
const apiKey =
process.env.HEALTHCLOUD_API_KEY;
if (!apiKey) {
throw new Error(
"HEALTHCLOUD_API_KEY is required."
);
}
stripeClient.setApiKey(
"x-api-key",
apiKey
);Parameters
| Parameter | Type | Description |
| ------------ | -------- | ------------------- |
| headerName | string | API key header name |
| value | string | API key value |
Notes
- Header name should typically be
x-api-key. - API key headers are attached only to Stripe connector requests.
Resources
Create PaymentIntent
Method Signature
stripeClient.createPaymentIntent(
request: PaymentIntentCreateRequest
): Promise<
PaymentIntentCreateResponse
>Behavior
Sends an authenticated POST request and creates an internal HealthCheck payment session plus a Stripe PaymentIntent.
The PaymentIntent is created without confirming it. The UI should confirm payment through Stripe SDK using the returned StripePublishableKey and PaymentIntentClientSecret.
Raw card details are never sent through this connector.
Parameters
| Parameter | Type | Description |
| --------- | ---------------------------- | ---------------------------------------- |
| request | PaymentIntentCreateRequest | Payment session and product information |
Request Fields
| Field | Type | Description |
| ------------- | -------- | ------------------------------------------------ |
| referenceId | string | Caller-defined payment reference |
| productId | string | Product or catalog item identifier |
| amount | number | Amount in the smallest currency unit |
| currency | string | Currency code, such as usd |
Returns
Returns the raw backend response:
PaymentIntentCreateResponseUsage
const response =
await stripeClient.createPaymentIntent({
referenceId: "order-test-001",
productId: "copay_40",
amount: 4000,
currency: "usd"
});
console.log(response.StripePublishableKey);
console.log(response.PaymentIntentClientSecret);API Request
{
"referenceId": "order-test-001",
"productId": "copay_40",
"amount": 4000,
"currency": "usd"
}API Response
{
"SessionId": "SESSION_ID",
"ReferenceId": "order-test-001",
"ExpiresAt": "2026-06-09T22:35:01Z",
"StripePublishableKey": "pk_test_...",
"StripePaymentIntentId": "pi_...",
"PaymentIntentClientSecret": "pi_..._secret_...",
"StripeCustomerId": "cus_...",
"Amount": 4000,
"Currency": "usd"
}Notes
- The connector preserves the raw backend response contract.
StripePublishableKeyis safe for the UI to use.PaymentIntentClientSecretis used by Stripe SDK to confirm the PaymentIntent.- The backend secret key is never exposed to the UI.
Complete PaymentIntent
Method Signature
stripeClient.completePaymentIntent(
request: PaymentIntentCompleteRequest
): Promise<
PaymentIntentStatusResponse
>Behavior
Sends an authenticated POST request and verifies the current Stripe PaymentIntent status for the authenticated patient session.
If Stripe reports succeeded, the backend saves the payment record and marks the HealthCheck payment session as consumed.
Parameters
| Parameter | Type | Description |
| --------- | ------------------------------ | --------------------------------------------- |
| request | PaymentIntentCompleteRequest | Payment session and Stripe PaymentIntent IDs |
Request Fields
| Field | Type | Description |
| ----------------------- | -------- | ----------------------------------------------- |
| sessionId | string | Session ID returned by createPaymentIntent |
| stripePaymentIntentId | string | Stripe PaymentIntent ID returned by create flow |
Returns
Returns the raw backend response:
PaymentIntentStatusResponseUsage
const response =
await stripeClient.completePaymentIntent({
sessionId: createResponse.SessionId,
stripePaymentIntentId:
createResponse.StripePaymentIntentId
});
console.log(response.Status);API Request
{
"sessionId": "SESSION_ID_FROM_CREATE_RESPONSE",
"stripePaymentIntentId": "PI_ID_FROM_CREATE_RESPONSE"
}API Response
Before the UI confirms the payment, the expected status is usually requires_payment_method.
{
"SessionId": "SESSION_ID",
"PaymentId": null,
"StripePaymentIntentId": "pi_...",
"Status": "requires_payment_method",
"Last4": null,
"Brand": null,
"ReceiptUrl": null
}After the UI confirms the payment through Stripe SDK, completePaymentIntent(...) should return succeeded.
{
"SessionId": "SESSION_ID",
"PaymentId": "PAYMENT_ID",
"StripePaymentIntentId": "pi_...",
"Status": "succeeded",
"Last4": "4242",
"Brand": "visa",
"ReceiptUrl": "https://pay.stripe.com/receipts/example"
}Notes
sessionIdshould be set fromSessionIdin thecreatePaymentIntentresponse.stripePaymentIntentIdshould be set fromStripePaymentIntentIdin thecreatePaymentIntentresponse.- The connector preserves the raw backend response contract.
Frontend Stripe Flow
The payment flow has three separate steps:
- Create a backend-owned PaymentIntent with
stripeClient.createPaymentIntent(...). - Confirm the payment in the frontend with Stripe SDK.
- Call
stripeClient.completePaymentIntent(...)to let the backend verify and save the final payment state.
Do not call completePaymentIntent(...) immediately after createPaymentIntent(...).
At that point Stripe usually still returns requires_payment_method, because the patient has not entered and confirmed a payment method yet.
Create the PaymentIntent
const createResponse =
await stripeClient.createPaymentIntent({
referenceId: "order-test-001",
productId: "copay_40",
amount: 4000,
currency: "usd"
});
const publishableKey =
createResponse.StripePublishableKey;
const clientSecret =
createResponse.PaymentIntentClientSecret;
const sessionId =
createResponse.SessionId;
const stripePaymentIntentId =
createResponse.StripePaymentIntentId;Use StripePublishableKey and PaymentIntentClientSecret only in the frontend Stripe integration.
Use SessionId and StripePaymentIntentId later when calling completePaymentIntent(...).
Confirm with Stripe in React
Install Stripe's frontend packages in the app that renders the payment form:
npm install @stripe/stripe-js @stripe/react-stripe-jsInitialize Stripe from the backend-provided publishable key:
import {
Elements,
PaymentElement,
useElements,
useStripe
} from "@stripe/react-stripe-js";
import { loadStripe } from "@stripe/stripe-js";
const stripePromise =
loadStripe(createResponse.StripePublishableKey);
function PaymentForm() {
const stripe = useStripe();
const elements = useElements();
async function confirmPayment() {
if (!stripe || !elements) {
return;
}
const submitResult =
await elements.submit();
if (submitResult.error) {
console.error(submitResult.error);
return;
}
const result =
await stripe.confirmPayment({
elements,
clientSecret: createResponse.PaymentIntentClientSecret,
confirmParams: {
return_url: window.location.href
},
redirect: "if_required"
});
if (result.error) {
console.error(result.error);
return;
}
console.log(result.paymentIntent?.status);
}
return (
<>
<PaymentElement />
<button onClick={confirmPayment}>
Pay
</button>
</>
);
}
function Checkout() {
return (
<Elements
stripe={stripePromise}
options={{
clientSecret:
createResponse.PaymentIntentClientSecret
}}
>
<PaymentForm />
</Elements>
);
}elements.submit() is required before stripe.confirmPayment(...) when using the Payment Element.
Call it as soon as the patient presses the pay button, before any additional asynchronous work.
Complete the PaymentIntent
After Stripe confirms the payment, call the backend completion method:
const completeResponse =
await stripeClient.completePaymentIntent({
sessionId: createResponse.SessionId,
stripePaymentIntentId:
createResponse.StripePaymentIntentId
});
console.log(completeResponse.Status);If the Stripe confirmation succeeded, the backend response should return succeeded and may include PaymentId, Last4, Brand, and ReceiptUrl.
If the response still returns requires_payment_method, the frontend Stripe confirmation did not complete successfully yet.
Test Cards
Use Stripe test cards only with test publishable keys.
4242 4242 4242 4242
Any future expiry date
Any three-digit CVC
Any postal codeFor example:
Card: 4242 4242 4242 4242
Expiry: 12/34
CVC: 123
Postal code: 10001Common Statuses
| Status | Meaning |
| ------ | ------- |
| requires_payment_method | The PaymentIntent exists, but the patient has not successfully confirmed a payment method yet. |
| requires_action | Stripe requires an additional step, such as 3D Secure authentication. |
| processing | Stripe accepted the payment and is processing it. |
| succeeded | The payment succeeded. Now call or retry completePaymentIntent(...) so the backend can save the final payment state. |
| canceled | The PaymentIntent can no longer be completed. Create a new PaymentIntent. |
Implementation Checklist
- Configure and authenticate
HCLoginClient. - Construct
HCStripeClientwith the samehttpClientandloginClient. - Call
createPaymentIntent(...). - Render Stripe Payment Element with
StripePublishableKeyandPaymentIntentClientSecret. - On pay button click, call
elements.submit(). - Call
stripe.confirmPayment(...). - When Stripe returns
succeededor a final successful state, callcompletePaymentIntent(...). - Never collect or send raw card details through this connector.
- Never expose backend Stripe secret keys in the app.
Notes
HCLoginClientmust be configured and authenticated before calling Stripe methods.Reuse the same authenticated
HCLoginClientinstance when constructingHCStripeClient.Public SDK methods do not require consumers to manually provide:
- authorization headers
- tenant identifiers
- resolved service URLs
- backend Stripe secret keys
The connector returns raw backend responses without response transformation or unwrapping.
Backend-defined failure responses remain part of the returned API contract.
Prerequisites
HCLoginClient must be configured and the patient must be logged in before calling any method on this connector.
import { HCLoginClient } from "@healthcloudai/hc-login-connector";
import { FetchClient } from "@healthcloudai/hc-http";
const httpClient = new FetchClient();
const loginClient = new HCLoginClient(httpClient);
loginClient.configure("healthcheck", "dev");
await loginClient.login("[email protected]", "ExamplePassword123!");See the hc-login-connector documentation for the full authentication flow.
Error Handling
All methods throw errors that extend APIError from @healthcloudai/hc-http.
Backend business failures are thrown as HCServiceError when the shared HTTP layer receives a backend error response.
import { HCServiceError, APIError } from "@healthcloudai/hc-http";
try {
const result =
await stripeClient.createPaymentIntent({
referenceId: "order-test-001",
productId: "copay_40",
amount: 4000,
currency: "usd"
});
console.log(result);
} catch (err) {
if (err instanceof HCServiceError) {
console.error("Backend error:", err.backendMessage);
} else if (err instanceof APIError) {
console.error("SDK error:", err.message, err.code);
}
}