Skip to content

Commit

Permalink
feat: added Payment Session Headless
Browse files Browse the repository at this point in the history
feat #207
  • Loading branch information
ArushKapoorJuspay committed Mar 11, 2024
1 parent 6d95afe commit 0eaac2a
Show file tree
Hide file tree
Showing 8 changed files with 577 additions and 330 deletions.
691 changes: 365 additions & 326 deletions src/Utilities/PaymentHelpers.res

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions src/Window.res
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,6 @@ let isSandbox = hostname === "beta.hyperswitch.io"
let isInteg = hostname === "dev.hyperswitch.io"

let isProd = hostname === "checkout.hyperswitch.io"

type location = {replace: (. string) => unit}
@val @scope("window") external location: location = "location"
29 changes: 29 additions & 0 deletions src/orca-loader/Hyper.res
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,34 @@ let make = (publishableKey, options: option<Js.Json.t>, analyticsInfo: option<Js
Window.paymentRequest(methodData, details, optionsForPaymentRequest)
}

let initPaymentSession = paymentSessionOptions => {
open Promise

let clientSecretId =
paymentSessionOptions
->Js.Json.decodeObject
->Belt.Option.flatMap(x => x->Js.Dict.get("clientSecret"))
->Belt.Option.flatMap(Js.Json.decodeString)
->Belt.Option.getWithDefault("")
clientSecret := clientSecretId
Js.Promise.make((~resolve, ~reject as _) => {
logger.setClientSecret(clientSecretId)
resolve(. Js.Json.null)
})
->then(_ => {
logger.setLogInfo(~value=Window.href, ~eventName=PAYMENT_SESSION_INITIATED, ())
resolve()
})
->ignore

PaymentSession.make(
paymentSessionOptions,
~clientSecret={clientSecretId},
~publishableKey,
~logger=Some(logger),
)
}

let returnObject = {
confirmOneClickPayment,
confirmPayment,
Expand All @@ -496,6 +524,7 @@ let make = (publishableKey, options: option<Js.Json.t>, analyticsInfo: option<Js
confirmCardPayment: confirmCardPaymentFn,
retrievePaymentIntent: retrievePaymentIntentFn,
paymentRequest,
initPaymentSession,
}
Window.setHyper(Window.window, returnObject)
returnObject
Expand Down
1 change: 1 addition & 0 deletions src/orca-loader/HyperLoader.res
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ let loadStripe = (str, option) => {

@val external window: {..} = "window"
window["Hyper"] = Hyper.make
window["Hyper"]["init"] = Hyper.make

let isWordpress = window["wp"] !== Js.Json.null
if !isWordpress {
Expand Down
5 changes: 1 addition & 4 deletions src/orca-loader/LoaderPaymentElement.res
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ open OrcaUtils

external eventToJson: Types.eventData => Js.Json.t = "%identity"

type location = {replace: (. string) => unit}
@val @scope("window") external location: location = "location"

@val @scope(("navigator", "clipboard"))
external writeText: string => Js.Promise.t<'a> = "writeText"

Expand Down Expand Up @@ -196,7 +193,7 @@ let make = (componentType, options, setIframeRef, iframeRef, mountPostMessage) =
switch eventDataObject->getOptionalJsonFromJson("openurl") {
| Some(val) => {
let url = val->getStringfromjson("")
location.replace(. url)
Window.location.replace(. url)
}
| None => ()
}
Expand Down
156 changes: 156 additions & 0 deletions src/orca-loader/PaymentSession.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
open Types

let make = (options, ~clientSecret, ~publishableKey, ~logger: option<OrcaLogger.loggerMake>) => {
open Promise
let logger = logger->Belt.Option.getWithDefault(OrcaLogger.defaultLoggerConfig)
let switchToCustomPod =
GlobalVars.isInteg &&
options
->Js.Json.decodeObject
->Belt.Option.flatMap(x => x->Js.Dict.get("switchToCustomPod"))
->Belt.Option.flatMap(Js.Json.decodeBoolean)
->Belt.Option.getWithDefault(false)
let endpoint = ApiEndpoint.getApiEndPoint(~publishableKey, ())

let defaultPaymentMethodList = ref(Js.Json.null)

let customerDetailsPromise = PaymentHelpers.useCustomerDetails(
~clientSecret,
~publishableKey,
~endpoint,
~switchToCustomPod,
~optLogger=Some(logger),
)

let getDefaultPaymentMethod = () => {
customerDetailsPromise
->then(customerDetails => {
let customerPaymentMethods =
customerDetails
->Js.Json.decodeObject
->Belt.Option.flatMap(x => x->Js.Dict.get("customer_payment_methods"))
->Belt.Option.flatMap(Js.Json.decodeArray)
->Belt.Option.getWithDefault([])
->Js.Array2.filter(customerPaymentMethod => {
customerPaymentMethod
->Js.Json.decodeObject
->Belt.Option.flatMap(x => x->Js.Dict.get("default_payment_method_set"))
->Belt.Option.flatMap(Js.Json.decodeBoolean)
->Belt.Option.getWithDefault(false)
})
let isGuestCustomer =
customerDetails
->Js.Json.decodeObject
->Belt.Option.flatMap(x => x->Js.Dict.get("is_guest_customer"))
->Belt.Option.getWithDefault(Js.Json.null)
let updatedCustomerDetails =
[
("customer_payment_methods", customerPaymentMethods->Js.Json.array),
("is_guest_customer", isGuestCustomer),
]
->Js.Dict.fromArray
->Js.Json.object_
defaultPaymentMethodList := updatedCustomerDetails
updatedCustomerDetails->resolve
})
->catch(_err => {
let dict =
[("customer_payment_methods", []->Js.Json.array)]->Js.Dict.fromArray->Js.Json.object_
let msg = [("customerPaymentMethods", dict)]->Js.Dict.fromArray
resolve(msg->Js.Json.object_)
})
}

let confirmWithDefault = payload => {
let customerPaymentMethod =
defaultPaymentMethodList.contents
->Utils.getDictFromJson
->Js.Dict.get("customer_payment_methods")
->Belt.Option.flatMap(Js.Json.decodeArray)
->Belt.Option.getWithDefault([])
->Belt.Array.get(0)
->Belt.Option.flatMap(Js.Json.decodeObject)
->Belt.Option.getWithDefault(Js.Dict.empty())
let paymentToken = customerPaymentMethod->Utils.getJsonFromDict("payment_token", Js.Json.null)
let paymentMethod = customerPaymentMethod->Utils.getJsonFromDict("payment_method", Js.Json.null)
let paymentMethodType =
customerPaymentMethod->Utils.getJsonFromDict("payment_method_type", Js.Json.null)

let confirmParams =
payload
->Js.Json.decodeObject
->Belt.Option.flatMap(x => x->Js.Dict.get("confirmParams"))
->Belt.Option.getWithDefault(Js.Json.null)

let returnUrl =
confirmParams
->Js.Json.decodeObject
->Belt.Option.flatMap(x => x->Js.Dict.get("return_url"))
->Belt.Option.flatMap(Js.Json.decodeString)
->Belt.Option.getWithDefault("")

let confirmParam: ConfirmType.confirmParams = {
return_url: returnUrl,
publishableKey,
}

let paymentIntentID = Js.String2.split(clientSecret, "_secret_")[0]
let endpoint = ApiEndpoint.getApiEndPoint(
~publishableKey=confirmParam.publishableKey,
~isConfirmCall=true,
(),
)
let uri = `${endpoint}/payments/${paymentIntentID}/confirm`
let headers = [("Content-Type", "application/json"), ("api-key", confirmParam.publishableKey)]

let paymentType: PaymentHelpers.payment = switch paymentMethodType
->Js.Json.decodeString
->Belt.Option.getWithDefault("") {
| "apple_pay" => Applepay
| "google_pay" => Gpay
| "debit"
| "credit"
| "" =>
Card
| _ => Other
}

let broswerInfo = BrowserSpec.broswerInfo()

let body = [
("client_secret", clientSecret->Js.Json.string),
("payment_method", paymentMethod),
("payment_token", paymentToken),
("payment_method_type", paymentMethodType),
]

let bodyStr =
body->Js.Array2.concat(broswerInfo)->Js.Dict.fromArray->Js.Json.object_->Js.Json.stringify

PaymentHelpers.intentCall(
~fetchApi=Utils.fetchApi,
~uri,
~headers,
~bodyStr,
~confirmParam: ConfirmType.confirmParams,
~clientSecret,
~optLogger=Some(logger),
~handleUserError=false,
~paymentType,
~iframeId="",
~fetchMethod=Fetch.Post,
~setIsManualRetryEnabled={(. _) => ()},
~switchToCustomPod=false,
~sdkHandleOneClickConfirmPayment=false,
~counter=0,
~isPaymentSession=true,
(),
)
}

let returnObject = {
getDefaultPaymentMethod,
confirmWithDefault,
}
returnObject
}
20 changes: 20 additions & 0 deletions src/orca-loader/Types.res
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ type element = {
create: (Js.Dict.key, Js.Json.t) => paymentElement,
}

type initPaymentSession = {
getDefaultPaymentMethod: unit => Promise.t<Js.Json.t>,
confirmWithDefault: Js.Json.t => Promise.t<Js.Json.t>,
}

type confirmParams = {return_url: string}

type confirmPaymentParams = {
Expand All @@ -56,6 +61,7 @@ type hyperInstance = {
retrievePaymentIntent: string => Js.Promise.t<Js.Json.t>,
widgets: Js.Json.t => element,
paymentRequest: Js.Json.t => Js.Json.t,
initPaymentSession: Js.Json.t => initPaymentSession,
}

let oneClickConfirmPaymentFn = (_, _) => {
Expand Down Expand Up @@ -115,6 +121,19 @@ let defaultElement = {
create,
}

let getDefaultPaymentMethod = () => {
Js.Promise.resolve(Js.Dict.empty()->Js.Json.object_)
}

let confirmWithDefault = _confirmParams => {
Js.Promise.resolve(Js.Dict.empty()->Js.Json.object_)
}

let defaultInitPaymentSession: initPaymentSession = {
getDefaultPaymentMethod,
confirmWithDefault,
}

let defaultHyperInstance = {
confirmOneClickPayment: oneClickConfirmPaymentFn,
confirmPayment: confirmPaymentFn,
Expand All @@ -123,6 +142,7 @@ let defaultHyperInstance = {
elements: _ev => defaultElement,
widgets: _ev => defaultElement,
paymentRequest: _ev => Js.Json.null,
initPaymentSession: _ev => defaultInitPaymentSession,
}

type eventType =
Expand Down
2 changes: 2 additions & 0 deletions src/orca-log-catcher/OrcaLogger.res
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type eventName =
| DISPLAY_VOUCHER
| PAYMENT_METHODS_RESPONSE
| LOADER_CHANGED
| PAYMENT_SESSION_INITIATED

let eventNameToStrMapper = eventName => {
switch eventName {
Expand Down Expand Up @@ -113,6 +114,7 @@ let eventNameToStrMapper = eventName => {
| DISPLAY_VOUCHER => "DISPLAY_VOUCHER"
| PAYMENT_METHODS_RESPONSE => "PAYMENT_METHODS_RESPONSE"
| LOADER_CHANGED => "LOADER_CHANGED"
| PAYMENT_SESSION_INITIATED => "PAYMENT_SESSION_INITIATED"
}
}

Expand Down

0 comments on commit 0eaac2a

Please sign in to comment.