diff --git a/.env.example b/.env.example index 4937eaf..3dd4d10 100644 --- a/.env.example +++ b/.env.example @@ -4,3 +4,4 @@ SECRET_KEY=aaaaaaaa APP_API_BASE_URL= APP_IFRAME_BASE_URL= +STOREFRONT_URL="" \ No newline at end of file diff --git a/src/lib/env.mjs b/src/lib/env.mjs index 1c6e98e..50cd941 100644 --- a/src/lib/env.mjs +++ b/src/lib/env.mjs @@ -26,6 +26,7 @@ export const env = createEnv({ REST_APL_TOKEN: z.string().optional(), APP_API_BASE_URL: z.string().optional(), APP_IFRAME_BASE_URL: z.string().optional(), + STOREFRONT_URL: z.string(), }, /* @@ -60,5 +61,6 @@ export const env = createEnv({ REST_APL_TOKEN: process.env.REST_APL_TOKEN, APP_API_BASE_URL: process.env.APP_API_BASE_URL, APP_IFRAME_BASE_URL: process.env.APP_IFRAME_BASE_URL, + STOREFRONT_URL: process.env.STOREFRONT_URL, }, }); diff --git a/src/modules/klarna/klarna-api.ts b/src/modules/klarna/klarna-api.ts index 009e063..90abb99 100644 --- a/src/modules/klarna/klarna-api.ts +++ b/src/modules/klarna/klarna-api.ts @@ -10,7 +10,9 @@ import { type components as PaymentComponents, } from "generated/klarna-payments"; import { type paths as OrdersPaths } from "generated/klarna-orders"; -type AllKlarnaPaths = PaymentPaths & OrdersPaths; +import { type paths as HppPaths } from "generated/klarna-hpp"; + +type AllKlarnaPaths = PaymentPaths & OrdersPaths & HppPaths; import { KlarnaHttpClientError } from "@/errors"; diff --git a/src/modules/webhooks/transaction-initialize-session.ts b/src/modules/webhooks/transaction-initialize-session.ts index 87cc550..460e3fb 100644 --- a/src/modules/webhooks/transaction-initialize-session.ts +++ b/src/modules/webhooks/transaction-initialize-session.ts @@ -1,26 +1,26 @@ -import { type TransactionInitializeSessionResponse } from "@/schemas/TransactionInitializeSession/TransactionInitializeSessionResponse.mjs"; -import { - TransactionFlowStrategyEnum, - type TransactionInitializeSessionEventFragment, -} from "generated/graphql"; +import { getNormalizedLocale } from "@/backend-lib/api-route-utils"; +import { KlarnaHttpClientError } from "@/errors"; +import { env } from "@/lib/env.mjs"; import { invariant } from "@/lib/invariant"; import { createLogger } from "@/lib/logger"; -import { getConfigurationForChannel } from "@/modules/payment-app-configuration/payment-app-configuration"; -import { getWebhookPaymentAppConfigurator } from "@/modules/payment-app-configuration/payment-app-configuration-factory"; -import { paymentAppFullyConfiguredEntrySchema } from "@/modules/payment-app-configuration/config-entry"; +import { obfuscateConfig } from "@/modules/app-configuration/utils"; +import { getKlarnaIntegerAmountFromSaleor } from "@/modules/klarna/currencies"; import { - getLineItems, getKlarnaApiClient, - type KlarnaMetadata, + getLineItems, prepareRequestAddress, + type KlarnaMetadata, } from "@/modules/klarna/klarna-api"; -import { type components } from "generated/klarna-payments"; -import { obfuscateConfig } from "@/modules/app-configuration/utils"; -import { type JSONObject } from "@/types"; -import { KlarnaHttpClientError } from "@/errors"; -import { getKlarnaIntegerAmountFromSaleor } from "@/modules/klarna/currencies"; -import { getNormalizedLocale } from "@/backend-lib/api-route-utils"; -import { env } from "@/lib/env.mjs"; +import { paymentAppFullyConfiguredEntrySchema } from "@/modules/payment-app-configuration/config-entry"; +import { getConfigurationForChannel } from "@/modules/payment-app-configuration/payment-app-configuration"; +import { getWebhookPaymentAppConfigurator } from "@/modules/payment-app-configuration/payment-app-configuration-factory"; +import { type TransactionInitializeSessionResponse } from "@/schemas/TransactionInitializeSession/TransactionInitializeSessionResponse.mjs"; +import { + TransactionFlowStrategyEnum, + type TransactionInitializeSessionEventFragment, +} from "generated/graphql"; +import { type components as hppComponents } from "generated/klarna-hpp"; +import { type components as paymentsComponents } from "generated/klarna-payments"; export const TransactionInitializeSessionWebhookHandler = async ( event: TransactionInitializeSessionEventFragment, @@ -63,6 +63,8 @@ export const TransactionInitializeSessionWebhookHandler = async ( const createKlarnaSession = klarnaClient.path("/payments/v1/sessions").method("post").create(); + const createHppSession = klarnaClient.path("/hpp/v1/sessions").method("post").create(); + const locale = getNormalizedLocale(event); const country = event.sourceObject.billingAddress?.country.code; @@ -88,7 +90,7 @@ export const TransactionInitializeSessionWebhookHandler = async ( authorizationCallbackUrl.searchParams.set("saleorApiUrl", saleorApiUrl); const email = sourceObject.userEmail; - const createKlarnaSessionPayload: components["schemas"]["session_create"] = { + const createKlarnaSessionPayload: paymentsComponents["schemas"]["session_create"] = { locale: locale.split("_")[0], purchase_country: country, purchase_currency: event.action.currency, @@ -105,21 +107,45 @@ export const TransactionInitializeSessionWebhookHandler = async ( authorization: authorizationCallbackUrl.toString(), }, }; + logger.info(authorizationCallbackUrl.toString()); logger.debug({ ...obfuscateConfig(createKlarnaSessionPayload) }, "createKlarnaSession payload"); const klarnaSession = await createKlarnaSession(createKlarnaSessionPayload); + logger.debug({ ...obfuscateConfig(klarnaSession) }, "createKlarnaSession result"); if (!klarnaSession.ok) { throw new KlarnaHttpClientError(klarnaSession.statusText, { errors: [klarnaSession.data] }); } + const createHppSessionPayload: hppComponents["schemas"]["SessionCreationRequestV1"] = { + payment_session_url: + klarnaConfig.apiUrl + "/payments/v1/sessions/" + klarnaSession.data.session_id, + merchant_urls: { + success: env.STOREFRONT_URL + `/${transactionId}?authorization_token={{authorization_token}}`, + cancel: env.STOREFRONT_URL + "/cancel", + back: env.STOREFRONT_URL + "/back", + failure: env.STOREFRONT_URL + "/failure", + error: env.STOREFRONT_URL + "/error", + }, + }; + + const klarnaHpp = await createHppSession(createHppSessionPayload); + + if (!klarnaHpp.ok) { + throw new KlarnaHttpClientError(klarnaHpp.statusText, { errors: [klarnaHpp.data] }); + } + + invariant(klarnaHpp.data.redirect_url, "Missing redirect_url in klarnaHpp response"); + const transactionInitializeSessionResponse: TransactionInitializeSessionResponse = { data: { - klarnaSessionResponse: klarnaSession.data as JSONObject, + klarnaHppResponse: { + redirectUrl: klarnaHpp.data.redirect_url, + }, }, - pspReference: klarnaSession.data.session_id, + pspReference: klarnaHpp.data.session_id, result: event.action.actionType === TransactionFlowStrategyEnum.Authorization ? "AUTHORIZATION_ACTION_REQUIRED" @@ -127,7 +153,7 @@ export const TransactionInitializeSessionWebhookHandler = async ( actions: [], amount: action.amount, message: "", - externalUrl: undefined, // @todo, + externalUrl: klarnaHpp.data.session_url, }; return transactionInitializeSessionResponse; }; diff --git a/src/schemas/TransactionInitializeSession/TransactionInitializeSessionResponse.schema.json b/src/schemas/TransactionInitializeSession/TransactionInitializeSessionResponse.schema.json index d84149e..7bea295 100644 --- a/src/schemas/TransactionInitializeSession/TransactionInitializeSessionResponse.schema.json +++ b/src/schemas/TransactionInitializeSession/TransactionInitializeSessionResponse.schema.json @@ -7,10 +7,10 @@ "type": "object", "additionalProperties": true, "properties": { - "klarnaSessionResponse": { "$ref": "definitions.json#/definitions/JSON" }, + "klarnaHppResponse": { "$ref": "definitions.json#/definitions/JSON" }, "errors": { "$ref": "definitions.json#/definitions/SyncWebhookAppErrors" } }, - "required": ["klarnaSessionResponse"] + "required": ["klarnaHppResponse"] }, "result": { "$ref": "definitions.json#/definitions/TransactionSessionResult" }, "amount": { "$ref": "definitions.json#/definitions/PositiveDecimal" },