"
+ lifecycle {
+ ignore_changes = [
+ value,
+ ]
+ }
+}
+
+
+#######################################################################
+## Fragment policy to extract user id from session token ##
+#######################################################################
+
+resource "azapi_resource" "pay_wallet_fragment_user_id_from_session_token" {
+
+ # provider = azapi.apim
+ type = "Microsoft.ApiManagement/service/policyFragments@2022-04-01-preview"
+ name = "pay-wallet-user-id-from-session-token"
+ parent_id = data.azurerm_api_management.apim.id
+
+ body = jsonencode({
+ properties = {
+ description = "Component that extract userId from JWT session token"
+ format = "rawxml"
+ value = templatefile("./api/fragments/_fragment_policy_user_id_from_session_token.tpl.xml", {
+ })
+
+ }
+ })
+
+ lifecycle {
+ ignore_changes = [output]
+ }
+}
diff --git a/src/domains/pay-wallet-app-v2/99_locals.tf b/src/domains/pay-wallet-app-v2/99_locals.tf
new file mode 100644
index 0000000000..65b5a4e124
--- /dev/null
+++ b/src/domains/pay-wallet-app-v2/99_locals.tf
@@ -0,0 +1,47 @@
+locals {
+ project = "${var.prefix}-${var.env_short}-${var.location_short}-${var.domain}"
+ product = "${var.prefix}-${var.env_short}"
+ product_italy = "${var.prefix}-${var.env_short}-${var.location_short}"
+
+
+ app_insights_ips_west_europe = [
+ "51.144.56.96/28",
+ "51.144.56.112/28",
+ "51.144.56.128/28",
+ "51.144.56.144/28",
+ "51.144.56.160/28",
+ "51.144.56.176/28",
+ ]
+
+ monitor_appinsights_italy_name = "${local.product_italy}-core-appinsights"
+ monitor_action_group_slack_name = "SlackPagoPA"
+ monitor_action_group_email_name = "PagoPA"
+ monitor_appinsights_name = "${local.product}-appinsights"
+
+ vnet_name = "${local.product}-vnet"
+ vnet_resource_group_name = "${local.product}-vnet-rg"
+
+ acr_name = replace("${local.product}commonacr", "-", "")
+ acr_resource_group_name = "${local.product}-container-registry-rg"
+
+ aks_name = "${local.product}-${var.location_short}-${var.instance}-aks"
+ aks_resource_group_name = "${local.product}-${var.location_short}-${var.instance}-aks-rg"
+
+ ingress_hostname = "${var.location_short}${var.instance}.${var.domain}"
+ internal_dns_zone_name = "${var.dns_zone_internal_prefix}.${var.external_domain}"
+
+ #pagopa_apim_name = "${local.product}-apim"
+ pagopa_apim_name = "${local.product}-weu-core-apim-v2"
+ pagopa_apim_rg = "${local.product}-api-rg"
+ pagopa_apim_snet = "${local.product}-apim-snet"
+
+ #pagopa_apim_v2_name = "${local.product}-weu-core-apim-v2"
+
+ pagopa_vnet_integration = "pagopa-${var.env_short}-vnet-integration"
+ pagopa_vnet_rg = "pagopa-${var.env_short}-vnet-rg"
+
+ apim_hostname = "api.${var.apim_dns_zone_prefix}.${var.external_domain}"
+ payment_wallet_hostname = var.env_short != "p" ? "${var.location_short}${var.env}.pay-wallet.internal.${var.env}.platform.pagopa.it" : "${var.location_short}${var.env}.pay-wallet.internal.platform.pagopa.it"
+ ecommerce_hostname = var.env_short == "p" ? "weu${var.env}.ecommerce.internal.platform.pagopa.it" : "weu${var.env}.ecommerce.internal.${var.env}.platform.pagopa.it"
+ wallet_service_hostname = var.env_short != "p" ? "weu${var.env}.wallet.internal.${var.env}.platform.pagopa.it" : "${var.location_short}${var.env}.pay-wallet.internal.platform.pagopa.it"
+}
diff --git a/src/domains/pay-wallet-app-v2/99_main.tf b/src/domains/pay-wallet-app-v2/99_main.tf
new file mode 100644
index 0000000000..5faabf1825
--- /dev/null
+++ b/src/domains/pay-wallet-app-v2/99_main.tf
@@ -0,0 +1,52 @@
+terraform {
+ required_providers {
+ azurerm = {
+ source = "hashicorp/azurerm"
+ version = "<= 3.95.0"
+ }
+ azuread = {
+ source = "hashicorp/azuread"
+ version = "<= 2.47.0"
+ }
+ null = {
+ source = "hashicorp/null"
+ version = "<= 3.2.1"
+ }
+ kubernetes = {
+ source = "hashicorp/kubernetes"
+ version = "<= 2.26.0"
+ }
+ helm = {
+ source = "hashicorp/helm"
+ version = "<= 2.12.0"
+ }
+ azapi = {
+ source = "azure/azapi"
+ version = "<= 1.3.0"
+ }
+ }
+
+ backend "azurerm" {}
+}
+
+provider "azurerm" {
+ features {
+ key_vault {
+ purge_soft_delete_on_destroy = false
+ }
+ }
+}
+
+data "azurerm_subscription" "current" {}
+
+data "azurerm_client_config" "current" {}
+
+provider "kubernetes" {
+ config_path = "${var.k8s_kube_config_path_prefix}/config-${local.aks_name}"
+}
+
+provider "helm" {
+ kubernetes {
+ config_path = "${var.k8s_kube_config_path_prefix}/config-${local.aks_name}"
+ }
+}
diff --git a/src/domains/pay-wallet-app-v2/99_variables.tf b/src/domains/pay-wallet-app-v2/99_variables.tf
new file mode 100644
index 0000000000..95e6f85798
--- /dev/null
+++ b/src/domains/pay-wallet-app-v2/99_variables.tf
@@ -0,0 +1,157 @@
+# general
+
+variable "prefix" {
+ type = string
+ validation {
+ condition = (
+ length(var.prefix) <= 6
+ )
+ error_message = "Max length is 6 chars."
+ }
+}
+
+variable "env" {
+ type = string
+}
+
+variable "env_short" {
+ type = string
+ validation {
+ condition = (
+ length(var.env_short) == 1
+ )
+ error_message = "Length must be 1 chars."
+ }
+}
+
+variable "domain" {
+ type = string
+ validation {
+ condition = (
+ length(var.domain) <= 12
+ )
+ error_message = "Max length is 12 chars."
+ }
+}
+
+variable "location" {
+ type = string
+ description = "One of westeurope, northeurope"
+}
+
+variable "location_short" {
+ type = string
+ validation {
+ condition = (
+ length(var.location_short) == 3
+ )
+ error_message = "Length must be 3 chars."
+ }
+ description = "One of wue, neu"
+}
+
+variable "location_string" {
+ type = string
+ description = "One of West Europe, North Europe"
+}
+
+variable "instance" {
+ type = string
+ description = "One of beta, prod01, prod02"
+}
+
+variable "tags" {
+ type = map(any)
+ default = {
+ CreatedBy = "Terraform"
+ }
+}
+
+### External resources
+
+variable "monitor_italy_resource_group_name" {
+ type = string
+ description = "Monitor Italy resource group name"
+}
+
+variable "log_analytics_italy_workspace_name" {
+ type = string
+ description = "Specifies the name of the Log Analytics Workspace Italy."
+}
+
+variable "log_analytics_italy_workspace_resource_group_name" {
+ type = string
+ description = "The name of the resource group in which the Log Analytics workspace Italy is located in."
+}
+
+### Aks
+
+variable "k8s_kube_config_path_prefix" {
+ type = string
+ default = "~/.kube"
+}
+
+variable "external_domain" {
+ type = string
+ default = null
+ description = "Domain for delegation"
+}
+
+variable "dns_zone_internal_prefix" {
+ type = string
+ default = null
+ description = "The dns subdomain."
+}
+
+variable "apim_dns_zone_prefix" {
+ type = string
+ default = null
+ description = "The dns subdomain for apim."
+}
+
+variable "tls_cert_check_helm" {
+ type = object({
+ chart_version = string,
+ image_name = string,
+ image_tag = string
+ })
+ description = "tls cert helm chart configuration"
+}
+
+variable "payment_wallet_with_pm_enabled" {
+ type = bool
+ default = false
+ description = "payment wallet using Payment Manager"
+}
+
+variable "pdv_api_base_path" {
+ type = string
+ default = null
+ description = "Personal data vault api base path"
+}
+
+variable "io_backend_base_path" {
+ type = string
+ default = null
+ description = "io backend api base path"
+}
+
+# DNS
+
+variable "dns_zone_prefix" {
+ type = string
+ default = null
+ description = "The wallet dns subdomain."
+}
+
+variable "payment_wallet_migrations_enabled" {
+ type = bool
+ default = false
+ description = "Payment wallet migrations enabled"
+}
+
+variable "enabled_payment_wallet_method_ids_pm" {
+ type = string
+ default = ""
+ description = "Comma separated list of eCommerce payment method ids that are enabled with PM APIs"
+}
diff --git a/src/domains/pay-wallet-app-v2/README.md b/src/domains/pay-wallet-app-v2/README.md
new file mode 100644
index 0000000000..0434b0d118
--- /dev/null
+++ b/src/domains/pay-wallet-app-v2/README.md
@@ -0,0 +1,85 @@
+# payment-wallet-app
+
+
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [azapi](#requirement\_azapi) | <= 1.3.0 |
+| [azuread](#requirement\_azuread) | <= 2.47.0 |
+| [azurerm](#requirement\_azurerm) | <= 3.95.0 |
+| [helm](#requirement\_helm) | <= 2.12.0 |
+| [kubernetes](#requirement\_kubernetes) | <= 2.26.0 |
+| [null](#requirement\_null) | <= 3.2.1 |
+
+## Modules
+
+| Name | Source | Version |
+|------|--------|---------|
+| [apim\_io\_payment\_wallet\_api\_v1](#module\_apim\_io\_payment\_wallet\_api\_v1) | git::https://github.com/pagopa/terraform-azurerm-v3.git//api_management_api | v6.3.0 |
+| [apim\_io\_payment\_wallet\_product](#module\_apim\_io\_payment\_wallet\_product) | git::https://github.com/pagopa/terraform-azurerm-v3.git//api_management_product | v6.3.0 |
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [azapi_resource.pay_wallet_fragment_user_id_from_session_token](https://registry.terraform.io/providers/azure/azapi/latest/docs/resources/resource) | resource |
+| [azurerm_api_management_api_operation_policy.delete_io_wallets](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_api_operation_policy) | resource |
+| [azurerm_api_management_api_operation_policy.get_payment_methods_for_io](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_api_operation_policy) | resource |
+| [azurerm_api_management_api_operation_policy.get_wallets_by_user_and_walletId_for_io](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_api_operation_policy) | resource |
+| [azurerm_api_management_api_operation_policy.get_wallets_by_user_for_io](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_api_operation_policy) | resource |
+| [azurerm_api_management_api_operation_policy.post_io_wallets](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_api_operation_policy) | resource |
+| [azurerm_api_management_api_operation_policy.update_applications_for_io](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_api_operation_policy) | resource |
+| [azurerm_api_management_api_version_set.io_payment_wallet_api](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_api_version_set) | resource |
+| [azurerm_api_management_named_value.pay_wallet_family_friends_user_ids](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_named_value) | resource |
+| [azurerm_api_management_named_value.wallet_personal_data_vault_api_key](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_named_value) | resource |
+| [azuread_group.adgroup_admin](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/data-sources/group) | data source |
+| [azuread_group.adgroup_developers](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/data-sources/group) | data source |
+| [azuread_group.adgroup_externals](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/data-sources/group) | data source |
+| [azuread_group.adgroup_security](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/data-sources/group) | data source |
+| [azurerm_api_management.apim](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/api_management) | data source |
+| [azurerm_application_insights.application_insights_italy](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/application_insights) | data source |
+| [azurerm_client_config.current](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/client_config) | data source |
+| [azurerm_key_vault.kv](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/key_vault) | data source |
+| [azurerm_key_vault_secret.personal_data_vault_api_key_secret](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/key_vault_secret) | data source |
+| [azurerm_log_analytics_workspace.log_analytics_italy](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/log_analytics_workspace) | data source |
+| [azurerm_monitor_action_group.email](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/monitor_action_group) | data source |
+| [azurerm_monitor_action_group.slack](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/monitor_action_group) | data source |
+| [azurerm_resource_group.monitor_italy_rg](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/resource_group) | data source |
+| [azurerm_subnet.apim_vnet](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/subnet) | data source |
+| [azurerm_subscription.current](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/subscription) | data source |
+| [azurerm_virtual_network.vnet](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/virtual_network) | data source |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [apim\_dns\_zone\_prefix](#input\_apim\_dns\_zone\_prefix) | The dns subdomain for apim. | `string` | `null` | no |
+| [dns\_zone\_internal\_prefix](#input\_dns\_zone\_internal\_prefix) | The dns subdomain. | `string` | `null` | no |
+| [dns\_zone\_prefix](#input\_dns\_zone\_prefix) | The wallet dns subdomain. | `string` | `null` | no |
+| [domain](#input\_domain) | n/a | `string` | n/a | yes |
+| [enabled\_payment\_wallet\_method\_ids\_pm](#input\_enabled\_payment\_wallet\_method\_ids\_pm) | Comma separated list of eCommerce payment method ids that are enabled with PM APIs | `string` | `""` | no |
+| [env](#input\_env) | n/a | `string` | n/a | yes |
+| [env\_short](#input\_env\_short) | n/a | `string` | n/a | yes |
+| [external\_domain](#input\_external\_domain) | Domain for delegation | `string` | `null` | no |
+| [instance](#input\_instance) | One of beta, prod01, prod02 | `string` | n/a | yes |
+| [io\_backend\_base\_path](#input\_io\_backend\_base\_path) | io backend api base path | `string` | `null` | no |
+| [k8s\_kube\_config\_path\_prefix](#input\_k8s\_kube\_config\_path\_prefix) | n/a | `string` | `"~/.kube"` | no |
+| [location](#input\_location) | One of westeurope, northeurope | `string` | n/a | yes |
+| [location\_short](#input\_location\_short) | One of wue, neu | `string` | n/a | yes |
+| [location\_string](#input\_location\_string) | One of West Europe, North Europe | `string` | n/a | yes |
+| [log\_analytics\_italy\_workspace\_name](#input\_log\_analytics\_italy\_workspace\_name) | Specifies the name of the Log Analytics Workspace Italy. | `string` | n/a | yes |
+| [log\_analytics\_italy\_workspace\_resource\_group\_name](#input\_log\_analytics\_italy\_workspace\_resource\_group\_name) | The name of the resource group in which the Log Analytics workspace Italy is located in. | `string` | n/a | yes |
+| [monitor\_italy\_resource\_group\_name](#input\_monitor\_italy\_resource\_group\_name) | Monitor Italy resource group name | `string` | n/a | yes |
+| [payment\_wallet\_migrations\_enabled](#input\_payment\_wallet\_migrations\_enabled) | Payment wallet migrations enabled | `bool` | `false` | no |
+| [payment\_wallet\_with\_pm\_enabled](#input\_payment\_wallet\_with\_pm\_enabled) | payment wallet using Payment Manager | `bool` | `false` | no |
+| [pdv\_api\_base\_path](#input\_pdv\_api\_base\_path) | Personal data vault api base path | `string` | `null` | no |
+| [prefix](#input\_prefix) | n/a | `string` | n/a | yes |
+| [tags](#input\_tags) | n/a | `map(any)` | {
"CreatedBy": "Terraform"
}
| no |
+| [tls\_cert\_check\_helm](#input\_tls\_cert\_check\_helm) | tls cert helm chart configuration | object({
chart_version = string,
image_name = string,
image_tag = string
})
| n/a | yes |
+
+## Outputs
+
+No outputs.
+
diff --git a/src/domains/pay-wallet-app-v2/api/fragments/_fragment_policy_user_id_from_session_token.tpl.xml b/src/domains/pay-wallet-app-v2/api/fragments/_fragment_policy_user_id_from_session_token.tpl.xml
new file mode 100644
index 0000000000..39eefad065
--- /dev/null
+++ b/src/domains/pay-wallet-app-v2/api/fragments/_fragment_policy_user_id_from_session_token.tpl.xml
@@ -0,0 +1,21 @@
+
+
+
+ 1 ? sessionTokenParts[1] : "";
+ int reminder = sessionTokenBody.Length % 4;
+ int toPad = reminder > 1 ? (4-reminder) : 0;
+ string padded = string.Concat(sessionTokenBody,string.Concat(Enumerable.Repeat("=", toPad)));
+ byte[] data = Convert.FromBase64String(padded);
+ string decodedString = System.Text.Encoding.UTF8.GetString(data);
+ Dictionary parsed = JsonConvert.DeserializeObject>(decodedString);
+ string userId = (string)parsed?["userId"];
+ return String.IsNullOrEmpty(userId) ? "user-id-not-found" : userId;
+ } catch(Exception){
+ return "user-id-not-found";
+ }
+ }" />
+
\ No newline at end of file
diff --git a/src/domains/pay-wallet-app-v2/api/io-payment-wallet/v1/_base_policy.xml.tpl b/src/domains/pay-wallet-app-v2/api/io-payment-wallet/v1/_base_policy.xml.tpl
new file mode 100644
index 0000000000..f755c4d77b
--- /dev/null
+++ b/src/domains/pay-wallet-app-v2/api/io-payment-wallet/v1/_base_policy.xml.tpl
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @((string)context.Variables.GetValueOrDefault("xUserId",""))
+
+
+
+
+
+
+ IO
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/domains/pay-wallet-app-v2/api/io-payment-wallet/v1/_delete_wallet.xml.tpl b/src/domains/pay-wallet-app-v2/api/io-payment-wallet/v1/_delete_wallet.xml.tpl
new file mode 100644
index 0000000000..1c57d097b0
--- /dev/null
+++ b/src/domains/pay-wallet-app-v2/api/io-payment-wallet/v1/_delete_wallet.xml.tpl
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+ @($"{{pm-host}}/pp-restapi-CD/v1/wallet/{(string)context.Variables["idWalletPM"]}")
+ DELETE
+
+ @($"Bearer {((String)context.Variables["sessionToken"])}")
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ application/json
+
+
+ {
+ "title": "Unauthorized",
+ "status": 401,
+ "detail": "Unauthorized"
+ }
+
+
+
+
+
+
+
+ application/json
+
+
+ {
+ "title": "Not Found",
+ "status": 404,
+ "detail": "Wallet not found"
+ }
+
+
+
+
+
+
+
+ application/json
+
+
+ {
+ "title": "Error deleting wallet",
+ "status": 502,
+ "detail": "There was an error deleting wallet"
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/domains/pay-wallet-app-v2/api/io-payment-wallet/v1/_get_payment_methods.xml.tpl b/src/domains/pay-wallet-app-v2/api/io-payment-wallet/v1/_get_payment_methods.xml.tpl
new file mode 100644
index 0000000000..372a549889
--- /dev/null
+++ b/src/domains/pay-wallet-app-v2/api/io-payment-wallet/v1/_get_payment_methods.xml.tpl
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @{
+ JObject response = context.Response.Body.As();
+
+ if (context.Response.StatusCode != 200) {
+ return response.ToString();
+ }
+
+ string enabled_payment_wallet_method_ids_pm = "${enabled_payment_wallet_method_ids_pm}";
+ string[] values = enabled_payment_wallet_method_ids_pm.Split(',');
+ HashSet pmEnabledMethods = new HashSet(values);
+
+ foreach (var method in ((JArray) response["paymentMethods"])) {
+ string id = (string) method["id"];
+ if (pmEnabledMethods.Contains(id)) {
+ method["status"] = "ENABLED";
+ method["methodManagement"] = "ONBOARDABLE_ONLY";
+ } else {
+ method["status"] = "DISABLED";
+ }
+ }
+
+ return response.ToString();
+ }
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/domains/pay-wallet-app-v2/api/io-payment-wallet/v1/_get_wallets_by_user.xml.tpl b/src/domains/pay-wallet-app-v2/api/io-payment-wallet/v1/_get_wallets_by_user.xml.tpl
new file mode 100644
index 0000000000..39fa423c1c
--- /dev/null
+++ b/src/domains/pay-wallet-app-v2/api/io-payment-wallet/v1/_get_wallets_by_user.xml.tpl
@@ -0,0 +1,207 @@
+
+
+
+
+
+
+
+ {{pm-host}}/pp-restapi-CD/v3/wallet
+ GET
+
+ @($"Bearer {((String)context.Variables["sessionToken"])}")
+
+
+
+
+
+
+
+ application/json
+
+ {
+ "title": "Unauthorized",
+ "status": 401,
+ "detail": "Unauthorized"
+ }
+
+
+
+
+
+
+ application/json
+
+
+ {
+ "title": "Error retrieving user wallet data",
+ "status": 502,
+ "detail": "There was an error retrieving user wallet data"
+ }
+
+
+
+
+ ())" />
+
+
+
+
+
+
+ application/json
+
+
+ {
+ "title": "Wallet not found",
+ "status": 404,
+ "detail": "No wallet found for input wallet token"
+ }
+
+
+
+
+
+
+
+ https://${ecommerce_hostname}/pagopa-ecommerce-payment-methods-service/payment-methods
+ GET
+
+ IO
+
+
+
+
+
+
+
+ application/json
+
+
+ {
+ "title": "Error retrieving eCommerce payment methods",
+ "status": 502,
+ "detail": "There was an error retrieving eCommerce payment methods"
+ }
+
+
+
+
+ ())" />
+
+
+
+
+ application/json
+
+
+ @{
+ JObject pmWalletResponse = (JObject)context.Variables["pmUserWalletResponseBody"];
+ var walletApplications = new List{"PAGOPA"};
+ var eCommerceWalletTypes = new Dictionary
+ {
+ { "Card", "CARDS" },
+ { "BPay", "BANCOMATPAY" },
+ { "PayPal", "PAYPAL" }
+ };
+ var eCommercePaymentMethodIds = new Dictionary();
+ JObject paymentMethods = (JObject)context.Variables["paymentMethodsResponseBody"];
+ foreach(JObject paymentMethod in (JArray)paymentMethods["paymentMethods"]){
+ eCommercePaymentMethodIds[paymentMethod["name"].ToString()] = paymentMethod["id"].ToString();
+ }
+ Object[] wallets = pmWalletResponse["data"]
+ .Where(wallet =>{
+ return eCommerceWalletTypes.ContainsKey((string) wallet["walletType"]);
+ })
+ .Select(wallet =>{
+ JObject result = new JObject();
+ //convert wallet id (long) to UUID v4 with all bit set to 0 (except for the version).
+ //wallet id long value is stored into UUID latest 8 byte
+ string walletIdHex = ((long)wallet["idWallet"]).ToString("X").PadLeft(16,'0');
+ string walletIdToUuid = "00000000-0000-4000-"+walletIdHex.Substring(0,4)+"-"+walletIdHex.Substring(4);
+ result["walletId"] = walletIdToUuid;
+ string pmWalletType = (string) wallet["walletType"];
+ string eCommerceWalletType = eCommerceWalletTypes[pmWalletType];
+ result["paymentMethodId"] = eCommercePaymentMethodIds[eCommerceWalletType];
+ result["status"] = "VALIDATED";
+
+ TimeZoneInfo zone = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time");
+
+ DateTime creationDateTime = DateTime.Parse(((string)wallet["createDate"]).Replace(" ","T"));
+ DateTime utcCreationDateTime = TimeZoneInfo.ConvertTimeToUtc(creationDateTime, zone);
+ DateTimeOffset creationDateTimeOffset = new DateTimeOffset(utcCreationDateTime);
+ result["creationDate"] = creationDateTimeOffset.ToString("o");
+ result["updateDate"] = result["creationDate"];
+
+ var convertedApplications = new List();
+ foreach(JValue application in wallet["enableableFunctions"]){
+ string applicationName = application.ToString().ToUpper();
+ if(walletApplications.Contains(applicationName) && wallet[application.ToString()] != null){
+ JObject converted = new JObject();
+ converted["name"] = applicationName;
+ converted["status"] = Convert.ToBoolean(wallet[application.ToString()]) == true ? "ENABLED" : "DISABLED";
+ converted["updateDate"] = result["creationDate"];
+ convertedApplications.Add(converted);
+ }
+ }
+ result["applications"] = JArray.FromObject(convertedApplications);
+ JObject details = new JObject();
+ details["type"] = eCommerceWalletType;
+ string paymentMethodAsset = null;
+ if (eCommerceWalletType == "CARDS") {
+ details["lastFourDigits"] = $"{wallet["info"]["blurredNumber"]}";
+ details["expiryDate"] = $"{(string)wallet["info"]["expireYear"]}{(string)wallet["info"]["expireMonth"]}";
+ details["brand"] = wallet["info"]["brand"];
+ paymentMethodAsset = (string)wallet["info"]["brandLogo"];
+ }
+ if (eCommerceWalletType == "PAYPAL") {
+ var info = (JObject)(wallet["info"]);
+ var pspArray = (JArray)(info["pspInfo"]);
+ var pspInfo = (JObject)(pspArray[0]);
+ details["pspId"] = pspInfo["abi"];
+ details["maskedEmail"] = pspInfo["email"];
+ details["pspBusinessName"] = pspInfo["ragioneSociale"];
+ paymentMethodAsset = "https://assets.cdn.platform.pagopa.it/apm/paypal.png";
+ }
+ if (eCommerceWalletType == "BANCOMATPAY") {
+ details["maskedNumber"] = wallet["info"]["numberObfuscated"];
+ details["instituteCode"] = wallet["info"]["instituteCode"];
+ details["bankName"] = wallet["info"]["bankName"];
+ paymentMethodAsset = (string)wallet["info"]["brandLogo"];
+ }
+ result["details"] = details;
+ result["paymentMethodAsset"] = paymentMethodAsset;
+
+ Boolean favourite = (Boolean) wallet["favourite"];
+ JObject clients = new JObject();
+ JObject clientIO = new JObject();
+ clientIO["status"] = "ENABLED";
+ if(favourite == true) {
+ DateTime localDateUtc = DateTime.UtcNow;
+ DateTimeOffset lastUsageDateTimeOffset = new DateTimeOffset(localDateUtc);
+ clientIO["lastUsage"] = lastUsageDateTimeOffset.ToString("o");
+ }
+ clients["IO"] = clientIO;
+ result["clients"] = clients;
+
+ return result;
+ }).ToArray();
+
+ JObject response = new JObject();
+ response["wallets"] = JArray.FromObject(wallets);
+ return response.ToString();
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/domains/pay-wallet-app-v2/api/io-payment-wallet/v1/_get_wallets_by_user_and_walletId.xml.tpl b/src/domains/pay-wallet-app-v2/api/io-payment-wallet/v1/_get_wallets_by_user_and_walletId.xml.tpl
new file mode 100644
index 0000000000..b03ec92afa
--- /dev/null
+++ b/src/domains/pay-wallet-app-v2/api/io-payment-wallet/v1/_get_wallets_by_user_and_walletId.xml.tpl
@@ -0,0 +1,221 @@
+
+
+
+
+
+
+
+
+ {{pm-host}}/pp-restapi-CD/v3/wallet
+ GET
+
+ @($"Bearer {((String)context.Variables["sessionToken"])}")
+
+
+
+
+
+
+
+ application/json
+
+ {
+ "title": "Unauthorized",
+ "status": 401,
+ "detail": "Unauthorized"
+ }
+
+
+
+
+
+
+ application/json
+
+ {
+ "title": "Error retrieving user wallet data",
+ "status": 502,
+ "detail": "There was an error retrieving user wallet data"
+ }
+
+
+
+ ())" />
+
+
+
+
+
+
+ application/json
+
+ {
+ "title": "Wallet not found",
+ "status": 404,
+ "detail": "No wallet found for input wallet token"
+ }
+
+
+
+
+
+
+ https://${ecommerce_hostname}/pagopa-ecommerce-payment-methods-service/payment-methods
+ GET
+
+ IO
+
+
+
+
+
+
+
+ application/json
+
+ {
+ "title": "Error retrieving eCommerce payment methods",
+ "status": 502,
+ "detail": "There was an error retrieving eCommerce payment methods"
+ }
+
+
+
+ ())" />
+
+ {"PAGOPA"};
+ var eCommerceWalletTypes = new Dictionary
+ {
+ { "Card", "CARDS" },
+ { "BPay", "BANCOMATPAY" },
+ { "PayPal", "PAYPAL" }
+ };
+ var eCommercePaymentMethodIds = new Dictionary();
+ JObject paymentMethods = (JObject)context.Variables["paymentMethodsResponseBody"];
+ foreach(JObject paymentMethod in (JArray)paymentMethods["paymentMethods"]){
+ eCommercePaymentMethodIds[paymentMethod["name"].ToString()] = paymentMethod["id"].ToString();
+ }
+ JObject walletResult = (JObject) pmWalletResponse["data"]
+ .Where(wallet =>{
+ return eCommerceWalletTypes.ContainsKey((string) wallet["walletType"]) && (((string)context.Variables["walletId"]).Equals((string)wallet["idWallet"]));
+ })
+ .Select(wallet =>{
+
+ JObject result = new JObject();
+ //convert wallet id (long) to UUID v4 with all bit set to 0 (except for the version).
+ //wallet id long value is stored into UUID latest 8 byte
+ string walletIdHex = ((long)wallet["idWallet"]).ToString("X").PadLeft(16,'0');
+ string walletIdToUuid = "00000000-0000-4000-"+walletIdHex.Substring(0,4)+"-"+walletIdHex.Substring(4);
+ result["walletId"] = walletIdToUuid;
+ string pmWalletType = (string) wallet["walletType"];
+ string eCommerceWalletType = eCommerceWalletTypes[pmWalletType];
+ result["paymentMethodId"] = eCommercePaymentMethodIds[eCommerceWalletType];
+ result["status"] = "VALIDATED";
+
+ TimeZoneInfo zone = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time");
+
+ DateTime creationDateTime = DateTime.Parse(((string)wallet["createDate"]).Replace(" ","T"));
+ DateTime utcCreationDateTime = TimeZoneInfo.ConvertTimeToUtc(creationDateTime, zone);
+ DateTimeOffset creationDateTimeOffset = new DateTimeOffset(utcCreationDateTime);
+ result["creationDate"] = creationDateTimeOffset.ToString("o");
+ result["updateDate"] = result["creationDate"];
+
+ string paymentMethodAsset=null;
+ var convertedApplications = new List();
+ foreach(JValue application in wallet["enableableFunctions"]){
+ string applicationName = application.ToString().ToUpper();
+ if(walletApplications.Contains(applicationName) && wallet[application.ToString()] != null){
+ JObject converted = new JObject();
+ converted["name"] = applicationName;
+ converted["status"] = Convert.ToBoolean(wallet[application.ToString()]) == true ? "ENABLED" : "DISABLED";
+ converted["updateDate"] = result["creationDate"];
+ convertedApplications.Add(converted);
+ }
+ }
+ result["applications"] = JArray.FromObject(convertedApplications);
+ JObject details = new JObject();
+ details["type"] = eCommerceWalletType;
+ if (eCommerceWalletType == "CARDS") {
+ details["lastFourDigits"] = $"{wallet["info"]["blurredNumber"]}";
+ details["expiryDate"] = $"{(string)wallet["info"]["expireYear"]}{(string)wallet["info"]["expireMonth"]}";
+ details["brand"] = wallet["info"]["brand"];
+ paymentMethodAsset = (string)wallet["info"]["brandLogo"];
+ }
+ if (eCommerceWalletType == "PAYPAL") {
+ var info = (JObject)(wallet["info"]);
+ var pspArray = (JArray)(info["pspInfo"]);
+ var pspInfo = (JObject)(pspArray[0]);
+ details["pspId"] = pspInfo["abi"];
+ details["maskedEmail"] = pspInfo["email"];
+ details["pspBusinessName"] = pspInfo["ragioneSociale"];
+ paymentMethodAsset = "https://assets.cdn.platform.pagopa.it/apm/paypal.png";
+ }
+ if (eCommerceWalletType == "BANCOMATPAY") {
+ details["maskedNumber"] = wallet["info"]["numberObfuscated"];
+ details["instituteCode"] = wallet["info"]["instituteCode"];
+ details["bankName"] = wallet["info"]["bankName"];
+ paymentMethodAsset = (string)wallet["info"]["brandLogo"];
+ }
+ result["details"] = details;
+ result["paymentMethodAsset"] = paymentMethodAsset;
+
+ Boolean favourite = (Boolean) wallet["favourite"];
+ JObject clients = new JObject();
+ JObject clientIO = new JObject();
+ clientIO["status"] = "ENABLED";
+ if(favourite == true) {
+ DateTime localDateUtc = DateTime.UtcNow;
+ DateTimeOffset lastUsageDateTimeOffset = new DateTimeOffset(localDateUtc);
+ clientIO["lastUsage"] = lastUsageDateTimeOffset.ToString("o");
+ }
+ clients["IO"] = clientIO;
+ result["clients"] = clients;
+
+ return result;
+
+ }).SingleOrDefault();
+ return walletResult;
+ }" />
+
+
+
+
+
+ application/json
+
+ @(((JObject)context.Variables["walletResponseBody"]).ToString())
+
+
+
+
+
+
+ application/json
+
+ {
+ "title": "Wallet not found",
+ "status": 404,
+ "detail": "Wallet not found"
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/domains/pay-wallet-app-v2/api/io-payment-wallet/v1/_openapi.json.tpl b/src/domains/pay-wallet-app-v2/api/io-payment-wallet/v1/_openapi.json.tpl
new file mode 100644
index 0000000000..a7a42b02c5
--- /dev/null
+++ b/src/domains/pay-wallet-app-v2/api/io-payment-wallet/v1/_openapi.json.tpl
@@ -0,0 +1,973 @@
+{
+ "openapi": "3.0.3",
+ "info": {
+ "title": "pagoPA IO Payment Wallet API",
+ "version": "0.0.1",
+ "description": "API to handle payment wallets PagoPA for App IO, where a wallet is triple between user identifier, payment instrument and applications (i.e pagoPA, bpd).\n\nThe wallet onboarding outcome and walletId are returned as query params to the app IO, for example \n/wallets/{walletId}/outcomes?outcome=0&walletId=123. The possible outcome are:\n- SUCCESS(0)\n- GENERIC_ERROR(1)\n- AUTH_ERROR(2)\n- TIMEOUT(4)\n- CANCELED_BY_USER(8)\n- INVALID_SESSION(14)",
+ "termsOfService": "https://pagopa.it/terms/"
+ },
+ "tags": [
+ {
+ "name": "paymentMethods",
+ "description": "Api's to retrive payment methods",
+ "externalDocs": {
+ "url": "https://pagopa.atlassian.net/wiki/spaces/WA/overview?homepageId=622658099",
+ "description": "Documentation"
+ }
+ },
+ {
+ "name": "wallets",
+ "description": "Api's to handle a wallet",
+ "externalDocs": {
+ "url": "https://pagopa.atlassian.net/wiki/spaces/WA/overview?homepageId=622658099",
+ "description": "Documentation"
+ }
+ }
+ ],
+ "servers": [
+ {
+ "url": "https://${hostname}"
+ }
+ ],
+ "paths": {
+ "/payment-methods": {
+ "get": {
+ "tags": [
+ "paymentMethods"
+ ],
+ "operationId": "getAllPaymentMethodsForIO",
+ "security": [
+ {
+ "pagoPAPlatformSessionToken": []
+ }
+ ],
+ "summary": "Retrieve all Payment Methods for IO",
+ "parameters": [
+ {
+ "name": "amount",
+ "in": "query",
+ "description": "Payment Amount expressed in eurocents",
+ "required": false,
+ "schema": {
+ "type": "number"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Payment method successfully retrieved",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/PaymentMethodsResponse"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Formally invalid input",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ProblemJson"
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Unauthorized: the provided token is not valid or expired.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ProblemJson"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "Payment methods not found",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ProblemJson"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal server error serving request",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ProblemJson"
+ }
+ }
+ }
+ },
+ "502": {
+ "description": "Gateway error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ProblemJson"
+ }
+ }
+ }
+ },
+ "504": {
+ "description": "Timeout serving request"
+ }
+ }
+ }
+ },
+ "/wallets": {
+ "post": {
+ "tags": [
+ "wallets"
+ ],
+ "summary": "Create a new wallet for IO",
+ "description": "Creates a new wallet for IO",
+ "operationId": "createIOPaymentWallet",
+ "security": [
+ {
+ "pagoPAPlatformSessionToken": []
+ }
+ ],
+ "requestBody": {
+ "description": "Create a new wallet for IO",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/WalletCreateRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "201": {
+ "description": "Wallet created successfully",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/WalletCreateResponse"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Formally invalid input",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ProblemJson"
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Unauthorized: the provided token is not valid or expired.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ProblemJson"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal server error serving request",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ProblemJson"
+ }
+ }
+ }
+ },
+ "502": {
+ "description": "Gateway error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ProblemJson"
+ }
+ }
+ }
+ },
+ "504": {
+ "description": "Timeout serving request"
+ }
+ }
+ },
+ "get": {
+ "tags": [
+ "wallets"
+ ],
+ "summary": "Get wallet by user identifier",
+ "description": "Returns a of wallets related to user",
+ "operationId": "getIOPaymentWalletsByIdUser",
+ "security": [
+ {
+ "pagoPAPlatformSessionToken": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Wallet retrieved successfully",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Wallets"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid input id",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ProblemJson"
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Unauthorized: the provided token is not valid or expired.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ProblemJson"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "Wallet not found"
+ },
+ "502": {
+ "description": "Bad gateway"
+ },
+ "504": {
+ "description": "Timeout serving request"
+ }
+ }
+ }
+ },
+ "/wallets/{walletId}": {
+ "get": {
+ "tags": [
+ "wallets"
+ ],
+ "summary": "Get wallet by id",
+ "description": "Returns a single wallet",
+ "operationId": "getIOPaymentWalletById",
+ "security": [
+ {
+ "pagoPAPlatformSessionToken": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "walletId",
+ "in": "path",
+ "description": "ID of wallet to return",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/WalletId"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Wallet retrieved successfully",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/WalletInfo"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid input id",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ProblemJson"
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Unauthorized: the provided token is not valid or expired.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ProblemJson"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "Wallet not found"
+ },
+ "504": {
+ "description": "Timeout serving request"
+ }
+ }
+ },
+ "delete": {
+ "tags": [
+ "wallets"
+ ],
+ "summary": "Delete wallet by id",
+ "description": "Returns a single wallet",
+ "operationId": "deleteIOPaymentWalletById",
+ "security": [
+ {
+ "pagoPAPlatformSessionToken": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "walletId",
+ "in": "path",
+ "description": "ID of wallet to return",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/WalletId"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "Wallet deleted successfully"
+ },
+ "400": {
+ "description": "Invalid input id",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ProblemJson"
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Unauthorized: the provided token is not valid or expired.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ProblemJson"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "Wallet not found"
+ },
+ "504": {
+ "description": "Timeout serving request"
+ }
+ }
+ }
+ },
+ "/wallets/{walletId}/applications": {
+ "put": {
+ "tags": [
+ "wallets"
+ ],
+ "summary": "Update wallet applications and their status",
+ "description": "Update wallet applications",
+ "operationId": "updateIOPaymentWalletApplicationsById",
+ "requestBody": {
+ "description": "Update wallet applications for the specified wallet",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/WalletApplicationUpdateRequest"
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "pagoPAPlatformSessionToken": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "walletId",
+ "in": "path",
+ "description": "ID of wallet to return",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/WalletId"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "Wallet updated successfully"
+ },
+ "400": {
+ "description": "Invalid input id",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ProblemJson"
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ProblemJson"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "Wallet not found"
+ },
+ "409": {
+ "description": "Wallet request is inconsistent with global application status (e.g. the user requested a application to be enabled but the application has a global status of disabled)",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/WalletApplicationsPartialUpdate"
+ }
+ }
+ }
+ },
+ "504": {
+ "description": "Timeout serving request"
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "WalletId": {
+ "description": "Wallet identifier",
+ "type": "string",
+ "format": "uuid"
+ },
+ "ApplicationId": {
+ "type": "string",
+ "description": "Id of applications"
+ },
+ "Application": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "$ref": "#/components/schemas/ApplicationId"
+ },
+ "status": {
+ "$ref": "#/components/schemas/ApplicationStatus"
+ },
+ "updateDate": {
+ "description": "Application last update date",
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ },
+ "ApplicationStatus": {
+ "type": "string",
+ "description": "Enumeration of wallet statuses",
+ "enum": [
+ "ENABLED",
+ "DISABLED",
+ "INCOMING"
+ ]
+ },
+ "WalletStatus": {
+ "type": "string",
+ "description": "Enumeration of wallet statuses",
+ "enum": [
+ "CREATED",
+ "INITIALIZED",
+ "VALIDATED",
+ "DELETED",
+ "ERROR"
+ ]
+ },
+ "WalletCreateRequest": {
+ "type": "object",
+ "description": "Wallet creation request",
+ "properties": {
+ "applications": {
+ "type": "array",
+ "description": "List of applications for which wallet is enabled",
+ "items": {
+ "$ref": "#/components/schemas/WalletApplicationId"
+ }
+ },
+ "useDiagnosticTracing": {
+ "type": "boolean"
+ },
+ "paymentMethodId": {
+ "type": "string",
+ "format": "uuid"
+ }
+ },
+ "required": [
+ "paymentMethodId",
+ "applications",
+ "useDiagnosticTracing"
+ ]
+ },
+ "WalletCreateResponse": {
+ "type": "object",
+ "description": "Wallet creation response",
+ "properties": {
+ "redirectUrl": {
+ "type": "string",
+ "format": "url",
+ "description": "Redirection URL to a payment gateway page where the user can input a payment instrument information",
+ "example": "http://localhost/inputPage"
+ }
+ },
+ "required": [
+ "walletId",
+ "redirectUrl"
+ ]
+ },
+ "WalletApplication": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "$ref": "#/components/schemas/WalletApplicationId"
+ },
+ "status": {
+ "$ref": "#/components/schemas/WalletApplicationStatus"
+ }
+ }
+ },
+ "WalletApplicationId": {
+ "type": "string",
+ "description": "Id of wallet application"
+ },
+ "WalletApplicationStatus": {
+ "type": "string",
+ "description": "Enumeration of wallet statuses",
+ "enum": [
+ "ENABLED",
+ "DISABLED"
+ ]
+ },
+ "WalletApplicationUpdateRequest": {
+ "type": "object",
+ "description": "Wallet update request",
+ "properties": {
+ "applications": {
+ "type": "array",
+ "description": "List of applications to update",
+ "items": {
+ "$ref": "#/components/schemas/WalletApplication"
+ }
+ }
+ }
+ },
+ "WalletApplicationsPartialUpdate": {
+ "type": "object",
+ "description": "Response for wallet applications partial update due to status conflicts",
+ "properties": {
+ "updatedApplications": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/WalletApplication"
+ }
+ },
+ "failedApplications": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/WalletApplication"
+ }
+ }
+ },
+ "example": {
+ "updatedApplications": [
+ {
+ "name": "PAGOPA",
+ "status": "ENABLED"
+ }
+ ],
+ "failedApplications": [
+ {
+ "name": "PARI",
+ "status": "DISABLED"
+ }
+ ]
+ }
+ },
+ "WalletApplicationInfo": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "$ref": "#/components/schemas/WalletApplicationId"
+ },
+ "status": {
+ "$ref": "#/components/schemas/WalletApplicationStatus"
+ }
+ },
+ "required": [
+ "name",
+ "status"
+ ]
+ },
+ "WalletClientStatus": {
+ "type": "string",
+ "description": "Enumeration of wallet client statuses",
+ "enum": [
+ "ENABLED",
+ "DISABLED"
+ ]
+ },
+ "WalletClient": {
+ "type": "object",
+ "properties": {
+ "status": {
+ "$ref": "#/components/schemas/WalletClientStatus"
+ },
+ "lastUsage": {
+ "type": "string",
+ "description": "Time of last usage of this wallet by the client",
+ "format": "date-time"
+ }
+ },
+ "required": [
+ "status"
+ ]
+ },
+ "WalletInfo": {
+ "type": "object",
+ "description": "Wallet information",
+ "properties": {
+ "walletId": {
+ "$ref": "#/components/schemas/WalletId"
+ },
+ "paymentMethodId": {
+ "description": "Payment method identifier",
+ "type": "string"
+ },
+ "status": {
+ "$ref": "#/components/schemas/WalletStatus"
+ },
+ "creationDate": {
+ "description": "Wallet creation date",
+ "type": "string",
+ "format": "date-time"
+ },
+ "updateDate": {
+ "description": "Wallet update date",
+ "type": "string",
+ "format": "date-time"
+ },
+ "applications": {
+ "description": "list of applications for which this wallet is created for",
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/WalletApplicationInfo"
+ }
+ },
+ "clients": {
+ "description": "Client-specific state (e.g. last usage) and configuration (enabled/disabled)",
+ "type": "object",
+ "additionalProperties": {
+ "$ref": "#/components/schemas/WalletClient"
+ }
+ },
+ "details": {
+ "$ref": "#/components/schemas/WalletInfoDetails"
+ },
+ "paymentMethodAsset": {
+ "description": "Payment method asset",
+ "type": "string",
+ "format": "uri",
+ "example": "http://logo.cdn/brandLogo"
+ }
+ },
+ "required": [
+ "walletId",
+ "paymentMethodId",
+ "status",
+ "creationDate",
+ "updateDate",
+ "applications",
+ "clients",
+ "paymentMethodAsset"
+ ]
+ },
+ "WalletInfoDetails": {
+ "description": "details for the specific payment instrument. This field is disciminated by the type field",
+ "oneOf": [
+ {
+ "type": "object",
+ "description": "Card payment instrument details",
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "Wallet details discriminator field. Fixed valued 'CARDS'"
+ },
+ "lastFourDigits": {
+ "description": "Card last 4 digits",
+ "type": "string",
+ "example": "9876"
+ },
+ "expiryDate": {
+ "type": "string",
+ "description": "Credit card expiry date. The date format is `YYYYMM`",
+ "pattern": "^[0-9]{6}$",
+ "example": "203012"
+ },
+ "brand": {
+ "description": "Payment instrument brand",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "lastFourDigits",
+ "expiryDate",
+ "brand"
+ ]
+ },
+ {
+ "type": "object",
+ "description": "Paypal instrument details",
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "Wallet details discriminator field. Fixed valued 'PAYPAL'"
+ },
+ "pspId": {
+ "description": "bank identifier",
+ "type": "string"
+ },
+ "pspBusinessName": {
+ "description": "PSP business name",
+ "type": "string"
+ },
+ "maskedEmail": {
+ "description": "email masked pan",
+ "type": "string",
+ "example": "test***@***test.it"
+ }
+ },
+ "required": [
+ "type",
+ "pspId",
+ "pspBusinessName"
+ ]
+ },
+ {
+ "type": "object",
+ "description": "Bancomat pay instrument details",
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "Wallet details discriminator field. Fixed valued 'BANCOMATPAY'"
+ },
+ "maskedNumber": {
+ "description": "masked number",
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 20,
+ "example": "+3938*******202"
+ },
+ "instituteCode": {
+ "description": "institute code",
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 5,
+ "example": "12345"
+ },
+ "bankName": {
+ "description": "bank name",
+ "type": "string",
+ "example": "banca di banca"
+ }
+ },
+ "required": [
+ "type",
+ "maskedNumber",
+ "instituteCode",
+ "bankName"
+ ]
+ }
+ ]
+ },
+ "Wallets": {
+ "type": "object",
+ "description": "Wallets information",
+ "properties": {
+ "wallets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/WalletInfo"
+ }
+ }
+ }
+ },
+ "ProblemJson": {
+ "description": "Body definition for error responses containing failure details",
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "format": "uri",
+ "description": "An absolute URI that identifies the problem type. When dereferenced,\nit SHOULD provide human-readable documentation for the problem type\n(e.g., using HTML).",
+ "default": "about:blank",
+ "example": "https://example.com/problem/constraint-violation"
+ },
+ "title": {
+ "type": "string",
+ "description": "A short, summary of the problem type. Written in english and readable\nfor engineers (usually not suited for non technical stakeholders and\nnot localized); example: Application Unavailable"
+ },
+ "status": {
+ "$ref": "#/components/schemas/HttpStatusCode"
+ },
+ "detail": {
+ "type": "string",
+ "description": "A human readable explanation specific to this occurrence of the\nproblem.",
+ "example": "There was an error processing the request"
+ },
+ "instance": {
+ "type": "string",
+ "format": "uri",
+ "description": "An absolute URI that identifies the specific occurrence of the problem.\nIt may or may not yield further information if dereferenced."
+ }
+ }
+ },
+ "HttpStatusCode": {
+ "type": "integer",
+ "format": "int32",
+ "description": "The HTTP status code generated by the origin server for this occurrence\nof the problem.",
+ "minimum": 100,
+ "maximum": 600,
+ "exclusiveMaximum": true,
+ "example": 502
+ },
+ "PaymentMethodsResponse": {
+ "type": "object",
+ "description": "Payment methods response",
+ "properties": {
+ "paymentMethods": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/PaymentMethodResponse"
+ }
+ }
+ }
+ },
+ "PaymentMethodResponse": {
+ "type": "object",
+ "description": "Payment method Response",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Payment method ID"
+ },
+ "name": {
+ "type": "string",
+ "description": "Payment method name"
+ },
+ "description": {
+ "type": "string",
+ "description": "Payment method description"
+ },
+ "asset": {
+ "type": "string",
+ "description": "Payment method asset name"
+ },
+ "status": {
+ "$ref": "#/components/schemas/PaymentMethodStatus"
+ },
+ "paymentTypeCode": {
+ "type": "string",
+ "description": "Payment method type code"
+ },
+ "methodManagement": {
+ "$ref": "#/components/schemas/PaymentMethodManagementType"
+ },
+ "ranges": {
+ "description": "Payment amount range in eurocents",
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "$ref": "#/components/schemas/Range"
+ }
+ },
+ "brandAssets": {
+ "description": "Brand assets map associated to the selected payment method",
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ },
+ "required": [
+ "id",
+ "name",
+ "description",
+ "status",
+ "paymentTypeCode",
+ "ranges",
+ "methodManagement"
+ ]
+ },
+ "PaymentMethodStatus": {
+ "type": "string",
+ "description": "Payment method status",
+ "enum": [
+ "ENABLED",
+ "DISABLED",
+ "INCOMING"
+ ]
+ },
+ "PaymentMethodManagementType": {
+ "type": "string",
+ "description": "Describes how to manage the payment method authorization flow in wallet and eCommerce domain:\n- REDIRECT if it must be managed with a redirect flow;\n- ONBOARDABLE if it must be managed with NPG and it is possible to save the payment method in the wallet, but also guest payment is accepted;\n- NOT_ONBOARDABLE if it must be managed with NPG but the method cannot be saved, only guest payment is accepted;\n- ONBOARDABLE_ONLY if it must be managed with NPG and it is mandatory to save the payment method in the wallet to use it. Guest payment isn't accepted;\n- ONBORDABLE_WITH_PAYMENT if it must be managed with NPG and it is possible to save it, to use it as guest payment, and to onboard it during the payment;",
+ "enum": [
+ "ONBOARDABLE",
+ "NOT_ONBOARDABLE",
+ "REDIRECT",
+ "ONBOARDABLE_ONLY",
+ "ONBOARDABLE_WITH_PAYMENT"
+ ]
+ },
+ "Range": {
+ "type": "object",
+ "description": "Payment amount range in cents",
+ "properties": {
+ "min": {
+ "type": "integer",
+ "format": "int64",
+ "minimum": 0,
+ "description": "Range min amount"
+ },
+ "max": {
+ "type": "integer",
+ "format": "int64",
+ "minimum": 0
+ }
+ }
+ }
+ },
+ "securitySchemes": {
+ "pagoPAPlatformSessionToken": {
+ "type": "http",
+ "scheme": "bearer",
+ "description": "session token according to pagoPA platform for IO"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/domains/pay-wallet-app-v2/api/io-payment-wallet/v1/_post_wallets.xml.tpl b/src/domains/pay-wallet-app-v2/api/io-payment-wallet/v1/_post_wallets.xml.tpl
new file mode 100644
index 0000000000..4806956069
--- /dev/null
+++ b/src/domains/pay-wallet-app-v2/api/io-payment-wallet/v1/_post_wallets.xml.tpl
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+
+
+ @("https://${ecommerce_hostname}/pagopa-ecommerce-payment-methods-service/payment-methods/" + context.Variables["paymentMethodId"])
+ GET
+
+ IO
+
+
+
+
+
+
+
+ application/json
+
+
+ {
+ "title": "Error retrieving eCommerce payment methods",
+ "status": 502,
+ "detail": "There was an error retrieving eCommerce payment methods"
+ }
+
+
+
+
+ ())" />
+
+
+ {
+ { "CP", "pm-onboarding/creditcard" },
+ { "BPAY", "pm-onboarding/bpay" },
+ { "PPAL", "pm-onboarding/paypal" }
+ };
+
+ string redirectUrlPrefix;
+ paymentMethodTypeCodes.TryGetValue(returnedPaymentMethodTypeCode, out redirectUrlPrefix);
+ return redirectUrlPrefix;
+ }" />
+
+
+
+
+
+ application/json
+
+
+ {
+ "title": "Error retrieving eCommerce payment methods",
+ "status": 502,
+ "detail": "Invalid payment method name"
+ }
+
+
+
+
+
+
+
+
+ application/json
+
+ @{
+ return new JObject(
+ new JProperty("redirectUrl", $"https://${env}payment-wallet.pagopa.it/{(string)context.Variables["redirectUrlPrefix"]}#sessionToken={((string) context.Variables["sessionToken"])}")
+ ).ToString();
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @{
+ JObject inBody = context.Response.Body.As(preserveContent: true);
+ var redirectUrl = inBody["redirectUrl"];
+ inBody["redirectUrl"] = redirectUrl + "&sessionToken=" + ((string)context.Variables.GetValueOrDefault("x-jwt-token",""));
+ inBody.Remove("walletId");
+ return inBody.ToString();
+ }
+
+
+
+
+
+
+
+
+
+
diff --git a/src/domains/pay-wallet-app-v2/api/io-payment-wallet/v1/_update_applications.xml.tpl b/src/domains/pay-wallet-app-v2/api/io-payment-wallet/v1/_update_applications.xml.tpl
new file mode 100644
index 0000000000..1d05867878
--- /dev/null
+++ b/src/domains/pay-wallet-app-v2/api/io-payment-wallet/v1/_update_applications.xml.tpl
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+ @($"{{pm-host}}/pp-restapi-CD/v2/wallet/{(string)context.Variables["idWalletPM"]}/payment-status")
+ PUT
+
+ application/json
+
+
+ @($"Bearer {((String)context.Variables["sessionToken"])}")
+
+ @{
+ JObject requestBody = (JObject)context.Variables["requestBody"];
+ JArray applications = (JArray)requestBody["applications"];
+ foreach(JObject application in applications){
+ String name = application["name"].ToString();
+ String status = application["status"].ToString();
+ if(name.Equals("PAGOPA")) {
+ return new JObject(
+ new JProperty("data", new JObject(
+ new JProperty("pagoPA", status.Equals("ENABLED"))
+ ))
+ ).ToString();
+ }
+ }
+ return new JObject().ToString();
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+ application/json
+
+
+ {
+ "title": "Unauthorized",
+ "status": 401,
+ "detail": "Unauthorized"
+ }
+
+
+
+
+
+
+
+ application/json
+
+
+ {
+ "title": "Forbidden",
+ "status": 403,
+ "detail": "Forbidden"
+ }
+
+
+
+
+
+
+
+ application/json
+
+
+ {
+ "title": "Not Found",
+ "status": 404,
+ "detail": "Wallet not found"
+ }
+
+
+
+
+
+
+
+ application/json
+
+
+ {
+ "title": "Error changing wallet status",
+ "status": 502,
+ "detail": "There was an error changing wallet status"
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/domains/pay-wallet-app-v2/api_product/_base_policy.xml b/src/domains/pay-wallet-app-v2/api_product/_base_policy.xml
new file mode 100644
index 0000000000..ce18a37436
--- /dev/null
+++ b/src/domains/pay-wallet-app-v2/api_product/_base_policy.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/domains/pay-wallet-app-v2/env/itn-prod/backend.ini b/src/domains/pay-wallet-app-v2/env/itn-prod/backend.ini
new file mode 100644
index 0000000000..ddda4bb50f
--- /dev/null
+++ b/src/domains/pay-wallet-app-v2/env/itn-prod/backend.ini
@@ -0,0 +1 @@
+subscription=prod-pagoPA
diff --git a/src/domains/pay-wallet-app-v2/env/itn-prod/backend.tfvars b/src/domains/pay-wallet-app-v2/env/itn-prod/backend.tfvars
new file mode 100644
index 0000000000..c2c27f7775
--- /dev/null
+++ b/src/domains/pay-wallet-app-v2/env/itn-prod/backend.tfvars
@@ -0,0 +1,4 @@
+resource_group_name = "terraform-state-rg"
+storage_account_name = "tfinfprodpagopa"
+container_name = "terraform-state"
+key = "pay-wallet-app-prod-v2.terraform.tfstate"
diff --git a/src/domains/pay-wallet-app-v2/env/itn-prod/terraform.tfvars b/src/domains/pay-wallet-app-v2/env/itn-prod/terraform.tfvars
new file mode 100644
index 0000000000..811ae181fa
--- /dev/null
+++ b/src/domains/pay-wallet-app-v2/env/itn-prod/terraform.tfvars
@@ -0,0 +1,47 @@
+prefix = "pagopa"
+env_short = "p"
+env = "prod"
+domain = "pay-wallet"
+location = "italynorth"
+location_short = "itn"
+location_string = "Italy North"
+instance = "prod"
+
+tags = {
+ CreatedBy = "Terraform"
+ Environment = "prod"
+ Owner = "pagoPA"
+ Source = "https://github.com/pagopa/pagopa-infra/tree/main/src/domains/pay-wallet-app"
+ CostCenter = "TS310 - PAGAMENTI & SERVIZI"
+}
+
+### External resources
+
+monitor_italy_resource_group_name = "pagopa-p-itn-core-monitor-rg"
+log_analytics_italy_workspace_name = "pagopa-p-itn-core-law"
+log_analytics_italy_workspace_resource_group_name = "pagopa-p-itn-core-monitor-rg"
+
+external_domain = "pagopa.it"
+dns_zone_internal_prefix = "internal.platform"
+dns_zone_prefix = "payment-wallet"
+apim_dns_zone_prefix = "platform"
+
+### Aks
+
+ingress_load_balancer_ip = "10.3.2.250"
+
+# chart releases: https://github.com/pagopa/aks-microservice-chart-blueprint/releases
+# image tags: https://github.com/pagopa/infra-ssl-check/releases
+tls_cert_check_helm = {
+ chart_version = "2.0.0"
+ image_name = "ghcr.io/pagopa/infra-ssl-check"
+ image_tag = "v1.3.4@sha256:c3d45736706c981493b6216451fc65e99a69d5d64409ccb1c4ca93fef57c921d"
+}
+
+pdv_api_base_path = "https://api.tokenizer.pdv.pagopa.it/tokenizer/v1"
+io_backend_base_path = "https://api-app.io.pagopa.it"
+
+payment_wallet_with_pm_enabled = true
+
+payment_wallet_migrations_enabled = true
+enabled_payment_wallet_method_ids_pm = "6920b555-c972-4e2b-980c-b0e0037a111a,0ff153c2-4c5e-49a5-8720-788b6f190264,b63dbc2b-0b89-4431-a196-a5d73ff7ce9c"
diff --git a/src/domains/pay-wallet-app-v2/helm/cert-mounter.yaml.tpl b/src/domains/pay-wallet-app-v2/helm/cert-mounter.yaml.tpl
new file mode 100644
index 0000000000..224cb4f111
--- /dev/null
+++ b/src/domains/pay-wallet-app-v2/helm/cert-mounter.yaml.tpl
@@ -0,0 +1,13 @@
+namespace: ${NAMESPACE}
+nameOverride: ""
+fullnameOverride: ""
+
+deployment:
+ create: true
+
+kvCertificatesName:
+ - ${CERTIFICATE_NAME}
+
+keyvault:
+ name: "pagopa-${ENV_SHORT}-${DOMAIN}-kv"
+ tenantId: "7788edaf-0346-4068-9d79-c868aed15b3d"
diff --git a/src/domains/pay-wallet-app-v2/terraform.sh b/src/domains/pay-wallet-app-v2/terraform.sh
new file mode 100755
index 0000000000..c19455653f
--- /dev/null
+++ b/src/domains/pay-wallet-app-v2/terraform.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+set -e
+
+action=$1
+env=$2
+shift 2
+other=$@
+
+if [ -z "$action" ]; then
+ echo "Missed action: init, apply, plan"
+ exit 0
+fi
+
+if [ -z "$env" ]; then
+ echo "env should be: dev, uat or prod."
+ exit 0
+fi
+
+source "./env/$env/backend.ini"
+az account set -s "${subscription}"
+
+if echo "init plan apply refresh import output state taint destroy" | grep -w $action > /dev/null; then
+ if [ $action = "init" ]; then
+ terraform $action -reconfigure -backend-config="./env/$env/backend.tfvars" $other
+ elif [ $action = "output" ] || [ $action = "state" ] || [ $action = "taint" ]; then
+ # init terraform backend
+ terraform init -reconfigure -backend-config="./env/$env/backend.tfvars"
+ terraform $action $other
+ else
+ # init terraform backend
+ terraform init -reconfigure -backend-config="./env/$env/backend.tfvars"
+ terraform $action -var-file="./env/$env/terraform.tfvars" $other
+ fi
+else
+ echo "Action not allowed."
+ exit 1
+fi
diff --git a/src/domains/pay-wallet-app/.terraform.lock.hcl b/src/domains/pay-wallet-app/.terraform.lock.hcl
index 80fe8f2b7e..792c4b3f64 100644
--- a/src/domains/pay-wallet-app/.terraform.lock.hcl
+++ b/src/domains/pay-wallet-app/.terraform.lock.hcl
@@ -1,6 +1,26 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
+provider "registry.terraform.io/azure/azapi" {
+ version = "1.3.0"
+ constraints = "<= 1.3.0"
+ hashes = [
+ "h1:OWZNYEGEIunmpxEcbGveH+kkdELQfMCUYxLt1b25UOc=",
+ "zh:0923b297c5b71ed584e5f3a0b2393e80244076e85102a90438159833353274b0",
+ "zh:11fa2922aa98ca55beaf7cc33c7edbde81bbd405fdfea2955276c7f5a8537240",
+ "zh:14af830fb6091d084bfc2711c8e9c7bf05aa3c56fe8fd8e2fb4eddeb345be88d",
+ "zh:25258425ecbffbdf09b0c8131d2c680cddd19b504e0036ee5f83972dcae7df0a",
+ "zh:2922b535fe4d4f0963189548f2f8360a0aaf951fd411354f2269a111d8a0c1ad",
+ "zh:32c9360305e00c25d0f9d0a84dfbdbad8da2465be769a9c1f11f132c0225358e",
+ "zh:4ddd3ee23c340d5000839d8d30ba7f94e695476d63075f95cfb041e67d8f6ef6",
+ "zh:5c1514392a5c3dd51084aa70cb6c4dcc8b027c4508b5e4eb9f8c3990fd403213",
+ "zh:6b3ecac7099ab86c007b5ad636bd029f5e5f3e9bd06b0f74c82f0451a7995ecc",
+ "zh:6cb7081745b378e910e0cf09fb5717a2ad35e629ce3e07415d6682c1c1407872",
+ "zh:7107eda5125c1b983380f1f6418c592fb7fb2eb5b589ad0e08f6c47341f36318",
+ "zh:c6fa7af32a7a47d23a85e0eea4d4cbb065378ae75aed8c9c628fb625b04bc619",
+ ]
+}
+
provider "registry.terraform.io/hashicorp/azuread" {
version = "2.47.0"
constraints = "<= 2.47.0"
@@ -29,6 +49,7 @@ provider "registry.terraform.io/hashicorp/azurerm" {
version = "3.45.0"
constraints = ">= 3.30.0, ~> 3.30, <= 3.45.0, <= 3.95.0, <= 3.97.1"
hashes = [
+ "h1:VQWxV5+qelZeUCjpdLvZ7iAom4RvG+fVVgK6ELvw/cs=",
"h1:gQLNY1I5e9kcle1p/VYEWb0eteQ/t5kUfnqVu2/GBNY=",
"zh:04c5dbb8845366ce5eb0dc2d55e151270cc2c0ace20993867fdae9af43b953ad",
"zh:2589585da615ccae341400d45d672ee3fae413fdd88449b5befeff12a85a44b2",
diff --git a/src/domains/pay-wallet-app/00_data.tf b/src/domains/pay-wallet-app/00_data.tf
new file mode 100644
index 0000000000..ae7bbf3b32
--- /dev/null
+++ b/src/domains/pay-wallet-app/00_data.tf
@@ -0,0 +1,4 @@
+data "azurerm_api_management" "apim" {
+ name = "${local.product}-apim"
+ resource_group_name = "${local.product}-api-rg"
+}
diff --git a/src/domains/pay-wallet-app/04_apim_io_payment_wallet.tf b/src/domains/pay-wallet-app/04_apim_io_payment_wallet.tf
index cac0ca577e..9ce11f35f0 100644
--- a/src/domains/pay-wallet-app/04_apim_io_payment_wallet.tf
+++ b/src/domains/pay-wallet-app/04_apim_io_payment_wallet.tf
@@ -84,7 +84,11 @@ resource "azurerm_api_management_api_operation_policy" "get_payment_methods_for_
api_management_name = local.pagopa_apim_name
operation_id = "getAllPaymentMethodsForIO"
- xml_content = templatefile("./api/io-payment-wallet/v1/_get_payment_methods.xml.tpl", { ecommerce_hostname = local.ecommerce_hostname }
+ xml_content = templatefile("./api/io-payment-wallet/v1/_get_payment_methods.xml.tpl",
+ {
+ ecommerce_hostname = local.ecommerce_hostname
+ enabled_payment_wallet_method_ids_pm = var.enabled_payment_wallet_method_ids_pm
+ }
)
}
@@ -113,7 +117,7 @@ resource "azurerm_api_management_api_operation_policy" "post_io_wallets" {
operation_id = "createIOPaymentWallet"
xml_content = templatefile("./api/io-payment-wallet/v1/_post_wallets.xml.tpl", {
- env = var.env,
+ env = var.env == "prod" ? "" : "${var.env}.",
ecommerce_hostname = local.ecommerce_hostname
})
}
@@ -126,3 +130,44 @@ resource "azurerm_api_management_api_operation_policy" "update_applications_for_
xml_content = file("./api/io-payment-wallet/v1/_update_applications.xml.tpl")
}
+
+
+resource "azurerm_api_management_named_value" "pay_wallet_family_friends_user_ids" {
+ name = "pay-wallet-family-friends-user-ids"
+ api_management_name = local.pagopa_apim_name
+ resource_group_name = local.pagopa_apim_rg
+ display_name = "pay-wallet-family-friends-user-ids"
+ value = ""
+ lifecycle {
+ ignore_changes = [
+ value,
+ ]
+ }
+}
+
+
+#######################################################################
+## Fragment policy to extract user id from session token ##
+#######################################################################
+
+resource "azapi_resource" "pay_wallet_fragment_user_id_from_session_token" {
+
+ # provider = azapi.apim
+ type = "Microsoft.ApiManagement/service/policyFragments@2022-04-01-preview"
+ name = "pay-wallet-user-id-from-session-token"
+ parent_id = data.azurerm_api_management.apim.id
+
+ body = jsonencode({
+ properties = {
+ description = "Component that extract userId from JWT session token"
+ format = "rawxml"
+ value = templatefile("./api/fragments/_fragment_policy_user_id_from_session_token.tpl.xml", {
+ })
+
+ }
+ })
+
+ lifecycle {
+ ignore_changes = [output]
+ }
+}
diff --git a/src/domains/pay-wallet-app/99_main.tf b/src/domains/pay-wallet-app/99_main.tf
index 2835595f8a..5faabf1825 100644
--- a/src/domains/pay-wallet-app/99_main.tf
+++ b/src/domains/pay-wallet-app/99_main.tf
@@ -20,6 +20,10 @@ terraform {
source = "hashicorp/helm"
version = "<= 2.12.0"
}
+ azapi = {
+ source = "azure/azapi"
+ version = "<= 1.3.0"
+ }
}
backend "azurerm" {}
diff --git a/src/domains/pay-wallet-app/99_variables.tf b/src/domains/pay-wallet-app/99_variables.tf
index 4970525d7a..95e6f85798 100644
--- a/src/domains/pay-wallet-app/99_variables.tf
+++ b/src/domains/pay-wallet-app/99_variables.tf
@@ -149,3 +149,9 @@ variable "payment_wallet_migrations_enabled" {
default = false
description = "Payment wallet migrations enabled"
}
+
+variable "enabled_payment_wallet_method_ids_pm" {
+ type = string
+ default = ""
+ description = "Comma separated list of eCommerce payment method ids that are enabled with PM APIs"
+}
diff --git a/src/domains/pay-wallet-app/README.md b/src/domains/pay-wallet-app/README.md
index 5ad6966bd4..17bcf749d3 100644
--- a/src/domains/pay-wallet-app/README.md
+++ b/src/domains/pay-wallet-app/README.md
@@ -6,6 +6,7 @@
| Name | Version |
|------|---------|
+| [azapi](#requirement\_azapi) | <= 1.3.0 |
| [azuread](#requirement\_azuread) | <= 2.47.0 |
| [azurerm](#requirement\_azurerm) | <= 3.95.0 |
| [helm](#requirement\_helm) | <= 2.12.0 |
@@ -34,6 +35,7 @@
| Name | Type |
|------|------|
+| [azapi_resource.pay_wallet_fragment_user_id_from_session_token](https://registry.terraform.io/providers/azure/azapi/latest/docs/resources/resource) | resource |
| [azurerm_api_management_api_operation_policy.create_wallet_pm](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_api_operation_policy) | resource |
| [azurerm_api_management_api_operation_policy.delete_io_wallets](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_api_operation_policy) | resource |
| [azurerm_api_management_api_operation_policy.delete_wallet](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_api_operation_policy) | resource |
@@ -57,6 +59,7 @@
| [azurerm_api_management_api_version_set.wallet_outcomes_api](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_api_version_set) | resource |
| [azurerm_api_management_api_version_set.wallet_webview_api](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_api_version_set) | resource |
| [azurerm_api_management_group.payment-wallet](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_group) | resource |
+| [azurerm_api_management_named_value.pay_wallet_family_friends_user_ids](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_named_value) | resource |
| [azurerm_api_management_named_value.wallet-jwt-signing-key](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_named_value) | resource |
| [azurerm_api_management_named_value.wallet_personal_data_vault_api_key](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_named_value) | resource |
| [azurerm_key_vault_secret.aks_apiserver_url](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_secret) | resource |
@@ -72,6 +75,7 @@
| [azuread_group.adgroup_developers](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/data-sources/group) | data source |
| [azuread_group.adgroup_externals](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/data-sources/group) | data source |
| [azuread_group.adgroup_security](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/data-sources/group) | data source |
+| [azurerm_api_management.apim](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/api_management) | data source |
| [azurerm_application_insights.application_insights_italy](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/application_insights) | data source |
| [azurerm_client_config.current](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/client_config) | data source |
| [azurerm_key_vault.kv](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/key_vault) | data source |
@@ -94,6 +98,7 @@
| [dns\_zone\_internal\_prefix](#input\_dns\_zone\_internal\_prefix) | The dns subdomain. | `string` | `null` | no |
| [dns\_zone\_prefix](#input\_dns\_zone\_prefix) | The wallet dns subdomain. | `string` | `null` | no |
| [domain](#input\_domain) | n/a | `string` | n/a | yes |
+| [enabled\_payment\_wallet\_method\_ids\_pm](#input\_enabled\_payment\_wallet\_method\_ids\_pm) | Comma separated list of eCommerce payment method ids that are enabled with PM APIs | `string` | `""` | no |
| [env](#input\_env) | n/a | `string` | n/a | yes |
| [env\_short](#input\_env\_short) | n/a | `string` | n/a | yes |
| [external\_domain](#input\_external\_domain) | Domain for delegation | `string` | `null` | no |
diff --git a/src/domains/pay-wallet-app/api/fragments/_fragment_policy_user_id_from_session_token.tpl.xml b/src/domains/pay-wallet-app/api/fragments/_fragment_policy_user_id_from_session_token.tpl.xml
new file mode 100644
index 0000000000..39eefad065
--- /dev/null
+++ b/src/domains/pay-wallet-app/api/fragments/_fragment_policy_user_id_from_session_token.tpl.xml
@@ -0,0 +1,21 @@
+
+
+
+ 1 ? sessionTokenParts[1] : "";
+ int reminder = sessionTokenBody.Length % 4;
+ int toPad = reminder > 1 ? (4-reminder) : 0;
+ string padded = string.Concat(sessionTokenBody,string.Concat(Enumerable.Repeat("=", toPad)));
+ byte[] data = Convert.FromBase64String(padded);
+ string decodedString = System.Text.Encoding.UTF8.GetString(data);
+ Dictionary parsed = JsonConvert.DeserializeObject>(decodedString);
+ string userId = (string)parsed?["userId"];
+ return String.IsNullOrEmpty(userId) ? "user-id-not-found" : userId;
+ } catch(Exception){
+ return "user-id-not-found";
+ }
+ }" />
+
\ No newline at end of file
diff --git a/src/domains/pay-wallet-app/api/io-payment-wallet/v1/_base_policy.xml.tpl b/src/domains/pay-wallet-app/api/io-payment-wallet/v1/_base_policy.xml.tpl
index a4ca40dcb8..f755c4d77b 100644
--- a/src/domains/pay-wallet-app/api/io-payment-wallet/v1/_base_policy.xml.tpl
+++ b/src/domains/pay-wallet-app/api/io-payment-wallet/v1/_base_policy.xml.tpl
@@ -1,8 +1,11 @@
+
+
-
+
diff --git a/src/domains/pay-wallet-app/api/io-payment-wallet/v1/_delete_wallet.xml.tpl b/src/domains/pay-wallet-app/api/io-payment-wallet/v1/_delete_wallet.xml.tpl
index 488077b9ce..1c57d097b0 100644
--- a/src/domains/pay-wallet-app/api/io-payment-wallet/v1/_delete_wallet.xml.tpl
+++ b/src/domains/pay-wallet-app/api/io-payment-wallet/v1/_delete_wallet.xml.tpl
@@ -2,7 +2,7 @@
-
+
-
+
@@ -10,6 +10,34 @@
+
+
+ @{
+ JObject response = context.Response.Body.As();
+
+ if (context.Response.StatusCode != 200) {
+ return response.ToString();
+ }
+
+ string enabled_payment_wallet_method_ids_pm = "${enabled_payment_wallet_method_ids_pm}";
+ string[] values = enabled_payment_wallet_method_ids_pm.Split(',');
+ HashSet pmEnabledMethods = new HashSet(values);
+
+ foreach (var method in ((JArray) response["paymentMethods"])) {
+ string id = (string) method["id"];
+ if (pmEnabledMethods.Contains(id)) {
+ method["status"] = "ENABLED";
+ method["methodManagement"] = "ONBOARDABLE_ONLY";
+ } else {
+ method["status"] = "DISABLED";
+ }
+ }
+
+ return response.ToString();
+ }
+
+
+
diff --git a/src/domains/pay-wallet-app/api/io-payment-wallet/v1/_get_wallets_by_user.xml.tpl b/src/domains/pay-wallet-app/api/io-payment-wallet/v1/_get_wallets_by_user.xml.tpl
index 36d7663de0..39fa423c1c 100644
--- a/src/domains/pay-wallet-app/api/io-payment-wallet/v1/_get_wallets_by_user.xml.tpl
+++ b/src/domains/pay-wallet-app/api/io-payment-wallet/v1/_get_wallets_by_user.xml.tpl
@@ -2,7 +2,7 @@
-
+
{{pm-host}}/pp-restapi-CD/v3/wallet
@@ -12,6 +12,19 @@
+
+
+
+
+ application/json
+
+ {
+ "title": "Unauthorized",
+ "status": 401,
+ "detail": "Unauthorized"
+ }
+
+
diff --git a/src/domains/pay-wallet-app/api/io-payment-wallet/v1/_get_wallets_by_user_and_walletId.xml.tpl b/src/domains/pay-wallet-app/api/io-payment-wallet/v1/_get_wallets_by_user_and_walletId.xml.tpl
index f0c845082b..b03ec92afa 100644
--- a/src/domains/pay-wallet-app/api/io-payment-wallet/v1/_get_wallets_by_user_and_walletId.xml.tpl
+++ b/src/domains/pay-wallet-app/api/io-payment-wallet/v1/_get_wallets_by_user_and_walletId.xml.tpl
@@ -1,8 +1,8 @@
-
+
-
+
+
+
+
+
+ application/json
+
+ {
+ "title": "Unauthorized",
+ "status": 401,
+ "detail": "Unauthorized"
+ }
+
+
application/json
-
- {
+ {
"title": "Error retrieving user wallet data",
"status": 502,
"detail": "There was an error retrieving user wallet data"
- }
-
+ }
@@ -42,13 +53,11 @@
application/json
-
- {
+ {
"title": "Wallet not found",
"status": 404,
"detail": "No wallet found for input wallet token"
- }
-
+ }
@@ -64,29 +73,21 @@
-
-
- application/json
-
-
- {
+
+
+ application/json
+
+ {
"title": "Error retrieving eCommerce payment methods",
"status": 502,
"detail": "There was an error retrieving eCommerce payment methods"
- }
-
+ }
())" />
-
-
-
- application/json
-
-
- @{
+ {"PAGOPA"};
var eCommerceWalletTypes = new Dictionary
@@ -178,12 +179,33 @@
return result;
- }).Single();
-
- return walletResult.ToString();
- }
-
-
+ }).SingleOrDefault();
+ return walletResult;
+ }" />
+
+
+
+
+
+ application/json
+
+ @(((JObject)context.Variables["walletResponseBody"]).ToString())
+
+
+
+
+
+
+ application/json
+
+ {
+ "title": "Wallet not found",
+ "status": 404,
+ "detail": "Wallet not found"
+ }
+
+
+
@@ -196,4 +218,4 @@
-
+
\ No newline at end of file
diff --git a/src/domains/pay-wallet-app/api/io-payment-wallet/v1/_post_wallets.xml.tpl b/src/domains/pay-wallet-app/api/io-payment-wallet/v1/_post_wallets.xml.tpl
index 5919cc24d8..4806956069 100644
--- a/src/domains/pay-wallet-app/api/io-payment-wallet/v1/_post_wallets.xml.tpl
+++ b/src/domains/pay-wallet-app/api/io-payment-wallet/v1/_post_wallets.xml.tpl
@@ -2,7 +2,7 @@
-
+
@@ -70,7 +70,7 @@
@{
return new JObject(
- new JProperty("redirectUrl", $"https://${env}.payment-wallet.pagopa.it/{(string)context.Variables["redirectUrlPrefix"]}#sessionToken={((string) context.Variables["sessionToken"])}")
+ new JProperty("redirectUrl", $"https://${env}payment-wallet.pagopa.it/{(string)context.Variables["redirectUrlPrefix"]}#sessionToken={((string) context.Variables["sessionToken"])}")
).ToString();
}