package com.myfatoorahreactnative.cardviewv3 import com.google.gson.Gson import com.google.gson.JsonElement import com.google.gson.JsonObject import com.myfatoorah.sdk.v3.entity.callback.MFCallbackCard import com.myfatoorah.sdk.v3.entity.payment.MFPaymentDetails import com.myfatoorah.sdk.v3.entity.session.MFGetSessionData import com.myfatoorah.sdk.v3.entity.session.MFSessionData import com.myfatoorah.sdk.v3.views.MFV3PaymentResult import com.myfatoorah.sdk.v3.views.MFV3VerifyResult /** * MFV3Serializers - hand-serializes the V3 SDK result models into JSON strings with * PascalCase keys matching the JS interfaces in src/MFModels.tsx. * * The SDK models are Decodable-only and live in another module, so this builds * JsonObject trees by hand (with `type` discriminators for the result enums) instead * of reusing the V2 `handleReadableMap`/`convertJsonStringToUppercase` helpers, which * only flip key casing and can't add discriminators or flatten nested fields. Leaf * models whose @SerializedName already match the JS PascalCase contract still go * through Gson's `toJsonTree`. */ internal object MFV3Serializers { private val gson = Gson() //#region Public entry points fun session(session: MFSessionData): String = json(sessionObject(session)) fun paymentResult(result: MFV3PaymentResult): String = json(paymentResultObject(result)) fun verifyResult(result: MFV3VerifyResult): String = json(verifyResultObject(result)) //#endregion //#region Result enums private fun paymentResultObject(result: MFV3PaymentResult): JsonObject = when (result) { is MFV3PaymentResult.Completed -> JsonObject().apply { addProperty("type", "completed") addTree("details", gson.toJsonTree(result.details)) } is MFV3PaymentResult.CardCollected -> JsonObject().apply { addProperty("type", "cardCollected") addString("sessionId", result.sessionId) addTree("card", cardDetailsObject(result.card)) } is MFV3PaymentResult.PendingBackendResolution -> JsonObject().apply { addProperty("type", "pendingBackendResolution") addString("paymentData", result.paymentData) addString("redirectionUrl", result.redirectionUrl) addString("paymentId", result.paymentId) } } private fun verifyResultObject(result: MFV3VerifyResult): JsonObject = when (result) { is MFV3VerifyResult.Verified -> JsonObject().apply { addProperty("type", "verified") addTree("session", getSessionDataObject(result.session)) } is MFV3VerifyResult.PendingBackendResolution -> JsonObject().apply { addProperty("type", "pendingBackendResolution") addString("sessionId", result.sessionId) addString("redirectionUrl", result.redirectionUrl) } } //#endregion //#region Session (POST /v3/sessions) private fun sessionObject(s: MFSessionData): JsonObject = JsonObject().apply { addString("SessionId", s.sessionId) addString("SessionExpiry", s.sessionExpiry) addString("EncryptionKey", s.encryptionKey) addString("OperationType", s.operationType) addTree("Order", s.order?.let(gson::toJsonTree)) addTree("Customer", s.customer?.let(gson::toJsonTree)) } //#endregion //#region Card (COLLECT_DETAILS) // Hand-built because MFCallbackCard uses camelCase @SerializedName while the JS // contract (MFV3CardDetails) is PascalCase. private fun cardDetailsObject(c: MFCallbackCard?): JsonObject? { if (c == null) return null return JsonObject().apply { addString("Brand", c.brand) addString("PanHash", c.panHash) addString("Token", c.token) addString("Number", c.number) addString("NameOnCard", c.nameOnCard) addString("ExpiryYear", c.expiryYear) addString("ExpiryMonth", c.expiryMonth) addString("Issuer", c.issuer) addString("IssuerCountry", c.issuerCountry) addString("FundingMethod", c.fundingMethod) addString("ProductName", c.productName) } } //#endregion //#region Verify (GET /v3/sessions/{id}) // Customer.Reference is flattened to top-level CustomerReference (mirrors iOS); // TransactionResult stays a nested object. private fun getSessionDataObject(d: MFGetSessionData): JsonObject = JsonObject().apply { addString("SessionExpiry", d.sessionExpiry) addProperty("IsUsed", d.isUsed) addString("OperationType", d.operationType) addTree("Order", d.order?.let(gson::toJsonTree)) addString("CustomerReference", d.customer?.reference) addTree("Card", d.card?.let(gson::toJsonTree)) addTree("TransactionResult", d.transactionResult?.let(gson::toJsonTree)) } //#endregion //#region Helpers private fun JsonObject.addString(key: String, value: String?) { if (value != null) addProperty(key, value) } private fun JsonObject.addTree(key: String, tree: JsonElement?) { if (tree != null && !tree.isJsonNull) add(key, tree) } private fun json(obj: JsonObject): String = try { gson.toJson(obj) } catch (e: Exception) { "{}" } //#endregion }