From c749bd9c5128f8b5db1a3de060401a4d5dc19109 Mon Sep 17 00:00:00 2001 From: Sayak Bhattacharya Date: Fri, 1 Aug 2025 18:11:09 +0530 Subject: [PATCH] feat(connector): [BLUECODE] Added Bluecode Wallet QR Code Redirect Payment Method (#8762) Co-authored-by: Sayak Bhattacharya Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- api-reference/v1/openapi_spec_v1.json | 15 + api-reference/v2/openapi_spec_v2.json | 15 + config/config.example.toml | 3 + config/deployments/integration_test.toml | 3 + config/deployments/production.toml | 5 +- config/deployments/sandbox.toml | 3 + config/development.toml | 4 +- config/docker_compose.toml | 4 +- crates/api_models/src/payments.rs | 9 +- crates/common_enums/src/connector_enums.rs | 10 +- crates/common_enums/src/enums.rs | 2 + crates/common_enums/src/transformers.rs | 1 + crates/connector_configs/src/connector.rs | 1 + .../connector_configs/toml/development.toml | 12 +- crates/connector_configs/toml/production.toml | 13 +- crates/connector_configs/toml/sandbox.toml | 12 +- crates/euclid/src/frontend/dir/enums.rs | 1 + crates/euclid/src/frontend/dir/lowering.rs | 1 + .../euclid/src/frontend/dir/transformers.rs | 1 + .../src/connectors/aci/transformers.rs | 1 + .../src/connectors/adyen.rs | 3 +- .../src/connectors/adyen/transformers.rs | 1 + .../src/connectors/airwallex/transformers.rs | 1 + .../authorizedotnet/transformers.rs | 2 + .../connectors/bankofamerica/transformers.rs | 2 + .../src/connectors/bluecode.rs | 265 ++++++++++++---- .../src/connectors/bluecode/transformers.rs | 291 +++++++++++++++--- .../src/connectors/bluesnap/transformers.rs | 1 + .../src/connectors/boku/transformers.rs | 1 + .../src/connectors/checkout/transformers.rs | 2 + .../connectors/cybersource/transformers.rs | 2 + .../src/connectors/fiuu/transformers.rs | 1 + .../src/connectors/globepay/transformers.rs | 1 + .../src/connectors/klarna.rs | 4 + .../src/connectors/mifinity/transformers.rs | 1 + .../connectors/multisafepay/transformers.rs | 3 + .../src/connectors/nexinets/transformers.rs | 1 + .../src/connectors/nmi/transformers.rs | 1 + .../src/connectors/noon/transformers.rs | 1 + .../src/connectors/novalnet/transformers.rs | 2 + .../src/connectors/nuvei/transformers.rs | 1 + .../src/connectors/payme/transformers.rs | 1 + .../src/connectors/paypal/transformers.rs | 2 + .../src/connectors/shift4/transformers.rs | 1 + .../src/connectors/square/transformers.rs | 1 + .../src/connectors/stripe/transformers.rs | 3 + .../src/connectors/wellsfargo/transformers.rs | 2 + .../src/connectors/worldpay/transformers.rs | 1 + .../src/connectors/zen/transformers.rs | 1 + crates/hyperswitch_connectors/src/utils.rs | 2 + .../src/payment_method_data.rs | 6 + crates/kgraph_utils/src/mca.rs | 1 + crates/kgraph_utils/src/transformers.rs | 1 + .../payment_connector_required_fields.rs | 20 ++ crates/payment_methods/src/helpers.rs | 1 + crates/router/src/connector/utils.rs | 2 + .../router/src/core/connector_validation.rs | 9 +- .../router/src/types/api/connector_mapping.rs | 6 +- .../src/types/connector_transformers.rs | 2 +- crates/router/src/types/transformers.rs | 3 +- loadtest/config/development.toml | 3 + 61 files changed, 642 insertions(+), 129 deletions(-) diff --git a/api-reference/v1/openapi_spec_v1.json b/api-reference/v1/openapi_spec_v1.json index d75071dc77..861c5ae23e 100644 --- a/api-reference/v1/openapi_spec_v1.json +++ b/api-reference/v1/openapi_spec_v1.json @@ -11675,6 +11675,7 @@ "billwerk", "bitpay", "bluesnap", + "bluecode", "boku", "braintree", "breadpay", @@ -21694,6 +21695,7 @@ "benefit", "bizum", "blik", + "bluecode", "boleto", "bca_bank_transfer", "bni_va", @@ -29277,6 +29279,7 @@ "bambora", "bamboraapac", "bluesnap", + "bluecode", "boku", "braintree", "breadpay", @@ -32040,6 +32043,18 @@ } } }, + { + "type": "object", + "required": [ + "bluecode_redirect" + ], + "properties": { + "bluecode_redirect": { + "type": "object", + "description": "The wallet data for Bluecode QR Code Redirect" + } + } + }, { "type": "object", "required": [ diff --git a/api-reference/v2/openapi_spec_v2.json b/api-reference/v2/openapi_spec_v2.json index 6602d2e281..5ec220952b 100644 --- a/api-reference/v2/openapi_spec_v2.json +++ b/api-reference/v2/openapi_spec_v2.json @@ -8237,6 +8237,7 @@ "billwerk", "bitpay", "bluesnap", + "bluecode", "boku", "braintree", "breadpay", @@ -17926,6 +17927,7 @@ "benefit", "bizum", "blik", + "bluecode", "boleto", "bca_bank_transfer", "bni_va", @@ -23048,6 +23050,7 @@ "bambora", "bamboraapac", "bluesnap", + "bluecode", "boku", "braintree", "breadpay", @@ -25696,6 +25699,18 @@ } } }, + { + "type": "object", + "required": [ + "bluecode_redirect" + ], + "properties": { + "bluecode_redirect": { + "type": "object", + "description": "The wallet data for Bluecode QR Code Redirect" + } + } + }, { "type": "object", "required": [ diff --git a/config/config.example.toml b/config/config.example.toml index 2dfb3ed72e..55ab867560 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -883,6 +883,9 @@ google_pay = { country = "AU,AT,BE,BR,CA,CN,HK,MY,NZ,SG,US", currency = "AUD,EUR [pm_filters.santander] pix = { country = "BR", currency = "BRL" } +[pm_filters.bluecode] +bluecode = { country = "AT,BE,BG,HR,CY,CZ,DK,EE,FI,FR,DE,GR,HU,IE,IT,LV,LT,LU,MT,NL,PL,PT,RO,SK,SI,ES,SE,IS,LI,NO", currency = "EUR" } + [connector_customer] connector_list = "adyen,facilitapay,gocardless,hyperswitch_vault,stax,stripe" payout_connector_list = "nomupay,stripe,wise" diff --git a/config/deployments/integration_test.toml b/config/deployments/integration_test.toml index 82851ce4f0..8dab325c15 100644 --- a/config/deployments/integration_test.toml +++ b/config/deployments/integration_test.toml @@ -748,6 +748,9 @@ eft = { country = "NG, ZA, GH, KE, CI", currency = "NGN, GHS, ZAR, KES, USD" } [pm_filters.santander] pix = { country = "BR", currency = "BRL" } +[pm_filters.bluecode] +bluecode = { country = "AT,BE,BG,HR,CY,CZ,DK,EE,FI,FR,DE,GR,HU,IE,IT,LV,LT,LU,MT,NL,PL,PT,RO,SK,SI,ES,SE,IS,LI,NO", currency = "EUR" } + [temp_locker_enable_config] bluesnap.payment_method = "card" nuvei.payment_method = "card" diff --git a/config/deployments/production.toml b/config/deployments/production.toml index c9aaa239cc..cfbf4d8204 100644 --- a/config/deployments/production.toml +++ b/config/deployments/production.toml @@ -43,7 +43,7 @@ billwerk.base_url = "https://api.reepay.com/" billwerk.secondary_base_url = "https://card.reepay.com/" bitpay.base_url = "https://bitpay.com" blackhawknetwork.base_url = "https://api.blackhawknetwork.com/" -bluecode.base_url = "https://dev.eorder.reloadhero.com/" +bluecode.base_url = "https://app.eorder.reloadhero.com/" bluesnap.base_url = "https://ws.bluesnap.com/" bluesnap.secondary_base_url = "https://pay.bluesnap.com/" boku.base_url = "https://country-api4-stage.boku.com" @@ -750,6 +750,9 @@ eft = { country = "NG, ZA, GH, KE, CI", currency = "NGN, GHS, ZAR, KES, USD" } [pm_filters.santander] pix = { country = "BR", currency = "BRL" } +[pm_filters.bluecode] +bluecode = { country = "AT,BE,BG,HR,CY,CZ,DK,EE,FI,FR,DE,GR,HU,IE,IT,LV,LT,LU,MT,NL,PL,PT,RO,SK,SI,ES,SE,IS,LI,NO", currency = "EUR" } + [payout_method_filters.adyenplatform] sepa = { country = "AT,BE,CH,CZ,DE,EE,ES,FI,FR,GB,HU,IE,IT,LT,LV,NL,NO,PL,PT,SE,SK", currency = "EUR,CZK,DKK,HUF,NOK,PLN,SEK,GBP,CHF" } credit = { country = "AT,BE,BG,CY,CZ,DE,DK,EE,ES,FI,FR,GB,GR,HR,HU,IE,IS,IT,LI,LT,LU,LV,MT,NL,NO,PL,PT,RO,SE,SI,SK,US", currency = "EUR,USD,GBP" } diff --git a/config/deployments/sandbox.toml b/config/deployments/sandbox.toml index 684f493b7a..f4344abb80 100644 --- a/config/deployments/sandbox.toml +++ b/config/deployments/sandbox.toml @@ -761,6 +761,9 @@ eft = { country = "NG, ZA, GH, KE, CI", currency = "NGN, GHS, ZAR, KES, USD" } [pm_filters.santander] pix = { country = "BR", currency = "BRL" } +[pm_filters.bluecode] +bluecode = { country = "AT,BE,BG,HR,CY,CZ,DK,EE,FI,FR,DE,GR,HU,IE,IT,LV,LT,LU,MT,NL,PL,PT,RO,SK,SI,ES,SE,IS,LI,NO", currency = "EUR" } + [payout_method_filters.stripe] ach = { country = "US", currency = "USD" } diff --git a/config/development.toml b/config/development.toml index b44d287a12..b3aa9fcb9a 100644 --- a/config/development.toml +++ b/config/development.toml @@ -817,11 +817,13 @@ apple_pay = { country = "EG, MA, ZA, AU, CN, HK, JP, MO, MY, MN, NZ, SG, TW, VN, debit = { country = "AF,DZ,AW,AU,AZ,BS,BH,BD,BB,BZ,BM,BT,BO,BA,BW,BR,BN,BG,BI,KH,CA,CV,KY,CL,CO,KM,CD,CR,CZ,DK,DJ,ST,DO,EC,EG,SV,ER,ET,FK,FJ,GM,GE,GH,GI,GT,GN,GY,HT,HN,HK,HU,IS,IN,ID,IR,IQ,IE,IL,IT,JM,JP,JO,KZ,KE,KW,LA,LB,LS,LR,LY,LT,MO,MK,MG,MW,MY,MV,MR,MU,MX,MD,MN,MA,MZ,MM,NA,NZ,NI,NG,KP,NO,AR,PK,PG,PY,PE,UY,PH,PL,GB,QA,OM,RO,RU,RW,WS,SG,ST,ZA,KR,LK,SH,SD,SR,SZ,SE,CH,SY,TW,TJ,TZ,TH,TT,TN,TR,UG,UA,US,UZ,VU,VE,VN,ZM,ZW", currency = "AFN,DZD,ANG,AWG,AUD,AZN,BSD,BHD,BDT,BBD,BZD,BMD,BTN,BOB,BAM,BWP,BRL,BND,BGN,BIF,KHR,CAD,CVE,KYD,XOF,XAF,XPF,CLP,COP,KMF,CDF,CRC,EUR,CZK,DKK,DJF,DOP,XCD,EGP,SVC,ERN,ETB,EUR,FKP,FJD,GMD,GEL,GHS,GIP,GTQ,GNF,GYD,HTG,HNL,HKD,HUF,ISK,INR,IDR,IRR,IQD,ILS,JMD,JPY,JOD,KZT,KES,KWD,LAK,LBP,LSL,LRD,LYD,MOP,MKD,MGA,MWK,MYR,MVR,MRU,MUR,MXN,MDL,MNT,MAD,MZN,MMK,NAD,NPR,NZD,NIO,NGN,KPW,NOK,ARS,PKR,PAB,PGK,PYG,PEN,UYU,PHP,PLN,GBP,QAR,OMR,RON,RUB,RWF,WST,SAR,RSD,SCR,SLL,SGD,STN,SBD,SOS,ZAR,KRW,LKR,SHP,SDG,SRD,SZL,SEK,CHF,SYP,TWD,TJS,TZS,THB,TOP,TTD,TND,TRY,TMT,AED,UGX,UAH,USD,UZS,VUV,VND,YER,CNY,ZMW,ZWL" } credit = { country = "AF,DZ,AW,AU,AZ,BS,BH,BD,BB,BZ,BM,BT,BO,BA,BW,BR,BN,BG,BI,KH,CA,CV,KY,CL,CO,KM,CD,CR,CZ,DK,DJ,ST,DO,EC,EG,SV,ER,ET,FK,FJ,GM,GE,GH,GI,GT,GN,GY,HT,HN,HK,HU,IS,IN,ID,IR,IQ,IE,IL,IT,JM,JP,JO,KZ,KE,KW,LA,LB,LS,LR,LY,LT,MO,MK,MG,MW,MY,MV,MR,MU,MX,MD,MN,MA,MZ,MM,NA,NZ,NI,NG,KP,NO,AR,PK,PG,PY,PE,UY,PH,PL,GB,QA,OM,RO,RU,RW,WS,SG,ST,ZA,KR,LK,SH,SD,SR,SZ,SE,CH,SY,TW,TJ,TZ,TH,TT,TN,TR,UG,UA,US,UZ,VU,VE,VN,ZM,ZW", currency = "AFN,DZD,ANG,AWG,AUD,AZN,BSD,BHD,BDT,BBD,BZD,BMD,BTN,BOB,BAM,BWP,BRL,BND,BGN,BIF,KHR,CAD,CVE,KYD,XOF,XAF,XPF,CLP,COP,KMF,CDF,CRC,EUR,CZK,DKK,DJF,DOP,XCD,EGP,SVC,ERN,ETB,EUR,FKP,FJD,GMD,GEL,GHS,GIP,GTQ,GNF,GYD,HTG,HNL,HKD,HUF,ISK,INR,IDR,IRR,IQD,ILS,JMD,JPY,JOD,KZT,KES,KWD,LAK,LBP,LSL,LRD,LYD,MOP,MKD,MGA,MWK,MYR,MVR,MRU,MUR,MXN,MDL,MNT,MAD,MZN,MMK,NAD,NPR,NZD,NIO,NGN,KPW,NOK,ARS,PKR,PAB,PGK,PYG,PEN,UYU,PHP,PLN,GBP,QAR,OMR,RON,RUB,RWF,WST,SAR,RSD,SCR,SLL,SGD,STN,SBD,SOS,ZAR,KRW,LKR,SHP,SDG,SRD,SZL,SEK,CHF,SYP,TWD,TJS,TZS,THB,TOP,TTD,TND,TRY,TMT,AED,UGX,UAH,USD,UZS,VUV,VND,YER,CNY,ZMW,ZWL" } - [pm_filters.worldpayvantiv] debit = { country = "AF,DZ,AW,AU,AZ,BS,BH,BD,BB,BZ,BM,BT,BO,BA,BW,BR,BN,BG,BI,KH,CA,CV,KY,CL,CO,KM,CD,CR,CZ,DK,DJ,ST,DO,EC,EG,SV,ER,ET,FK,FJ,GM,GE,GH,GI,GT,GN,GY,HT,HN,HK,HU,IS,IN,ID,IR,IQ,IE,IL,IT,JM,JP,JO,KZ,KE,KW,LA,LB,LS,LR,LY,LT,MO,MK,MG,MW,MY,MV,MR,MU,MX,MD,MN,MA,MZ,MM,NA,NZ,NI,NG,KP,NO,AR,PK,PG,PY,PE,UY,PH,PL,GB,QA,OM,RO,RU,RW,WS,SG,ST,ZA,KR,LK,SH,SD,SR,SZ,SE,CH,SY,TW,TJ,TZ,TH,TT,TN,TR,UG,UA,US,UZ,VU,VE,VN,ZM,ZW", currency = "AFN,DZD,ANG,AWG,AUD,AZN,BSD,BHD,BDT,BBD,BZD,BMD,BTN,BOB,BAM,BWP,BRL,BND,BGN,BIF,KHR,CAD,CVE,KYD,XOF,XAF,XPF,CLP,COP,KMF,CDF,CRC,EUR,CZK,DKK,DJF,DOP,XCD,EGP,SVC,ERN,ETB,EUR,FKP,FJD,GMD,GEL,GHS,GIP,GTQ,GNF,GYD,HTG,HNL,HKD,HUF,ISK,INR,IDR,IRR,IQD,ILS,JMD,JPY,JOD,KZT,KES,KWD,LAK,LBP,LSL,LRD,LYD,MOP,MKD,MGA,MWK,MYR,MVR,MRU,MUR,MXN,MDL,MNT,MAD,MZN,MMK,NAD,NPR,NZD,NIO,NGN,KPW,NOK,ARS,PKR,PAB,PGK,PYG,PEN,UYU,PHP,PLN,GBP,QAR,OMR,RON,RUB,RWF,WST,SAR,RSD,SCR,SLL,SGD,STN,SBD,SOS,ZAR,KRW,LKR,SHP,SDG,SRD,SZL,SEK,CHF,SYP,TWD,TJS,TZS,THB,TOP,TTD,TND,TRY,TMT,AED,UGX,UAH,USD,UZS,VUV,VND,YER,CNY,ZMW,ZWL" } credit = { country = "AF,DZ,AW,AU,AZ,BS,BH,BD,BB,BZ,BM,BT,BO,BA,BW,BR,BN,BG,BI,KH,CA,CV,KY,CL,CO,KM,CD,CR,CZ,DK,DJ,ST,DO,EC,EG,SV,ER,ET,FK,FJ,GM,GE,GH,GI,GT,GN,GY,HT,HN,HK,HU,IS,IN,ID,IR,IQ,IE,IL,IT,JM,JP,JO,KZ,KE,KW,LA,LB,LS,LR,LY,LT,MO,MK,MG,MW,MY,MV,MR,MU,MX,MD,MN,MA,MZ,MM,NA,NZ,NI,NG,KP,NO,AR,PK,PG,PY,PE,UY,PH,PL,GB,QA,OM,RO,RU,RW,WS,SG,ST,ZA,KR,LK,SH,SD,SR,SZ,SE,CH,SY,TW,TJ,TZ,TH,TT,TN,TR,UG,UA,US,UZ,VU,VE,VN,ZM,ZW", currency = "AFN,DZD,ANG,AWG,AUD,AZN,BSD,BHD,BDT,BBD,BZD,BMD,BTN,BOB,BAM,BWP,BRL,BND,BGN,BIF,KHR,CAD,CVE,KYD,XOF,XAF,XPF,CLP,COP,KMF,CDF,CRC,EUR,CZK,DKK,DJF,DOP,XCD,EGP,SVC,ERN,ETB,EUR,FKP,FJD,GMD,GEL,GHS,GIP,GTQ,GNF,GYD,HTG,HNL,HKD,HUF,ISK,INR,IDR,IRR,IQD,ILS,JMD,JPY,JOD,KZT,KES,KWD,LAK,LBP,LSL,LRD,LYD,MOP,MKD,MGA,MWK,MYR,MVR,MRU,MUR,MXN,MDL,MNT,MAD,MZN,MMK,NAD,NPR,NZD,NIO,NGN,KPW,NOK,ARS,PKR,PAB,PGK,PYG,PEN,UYU,PHP,PLN,GBP,QAR,OMR,RON,RUB,RWF,WST,SAR,RSD,SCR,SLL,SGD,STN,SBD,SOS,ZAR,KRW,LKR,SHP,SDG,SRD,SZL,SEK,CHF,SYP,TWD,TJS,TZS,THB,TOP,TTD,TND,TRY,TMT,AED,UGX,UAH,USD,UZS,VUV,VND,YER,CNY,ZMW,ZWL" } +[pm_filters.bluecode] +bluecode = { country = "AT,BE,BG,HR,CY,CZ,DK,EE,FI,FR,DE,GR,HU,IE,IT,LV,LT,LU,MT,NL,PL,PT,RO,SK,SI,ES,SE,IS,LI,NO", currency = "EUR" } + [file_upload_config] bucket_name = "" region = "" diff --git a/config/docker_compose.toml b/config/docker_compose.toml index cc37750216..09ef545f58 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -637,7 +637,6 @@ paze = { currency = "USD,SEK" } ali_pay = { country = "GB",currency = "GBP,CNY" } we_chat_pay = { country = "GB",currency = "GBP,CNY" } - [pm_filters.itaubank] pix = { country = "BR", currency = "BRL" } @@ -886,6 +885,9 @@ eft = { country = "NG, ZA, GH, KE, CI", currency = "NGN, GHS, ZAR, KES, USD" } [pm_filters.santander] pix = { country = "BR", currency = "BRL" } +[pm_filters.bluecode] +bluecode = { country = "AT,BE,BG,HR,CY,CZ,DK,EE,FI,FR,DE,GR,HU,IE,IT,LV,LT,LU,MT,NL,PL,PT,RO,SK,SI,ES,SE,IS,LI,NO", currency = "EUR" } + [bank_config.online_banking_fpx] adyen.banks = "affin_bank,agro_bank,alliance_bank,am_bank,bank_islam,bank_muamalat,bank_rakyat,bank_simpanan_nasional,cimb_bank,hong_leong_bank,hsbc_bank,kuwait_finance_house,maybank,ocbc_bank,public_bank,rhb_bank,standard_chartered_bank,uob_bank" fiuu.banks = "affin_bank,agro_bank,alliance_bank,am_bank,bank_of_china,bank_islam,bank_muamalat,bank_rakyat,bank_simpanan_nasional,cimb_bank,hong_leong_bank,hsbc_bank,kuwait_finance_house,maybank,ocbc_bank,public_bank,rhb_bank,standard_chartered_bank,uob_bank" diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 0176553c5c..0c238d18c2 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -2670,6 +2670,7 @@ impl GetPaymentMethodType for MobilePaymentData { impl GetPaymentMethodType for WalletData { fn get_payment_method_type(&self) -> api_enums::PaymentMethodType { match self { + Self::BluecodeRedirect {} => api_enums::PaymentMethodType::Bluecode, Self::AliPayQr(_) | Self::AliPayRedirect(_) => api_enums::PaymentMethodType::AliPay, Self::AliPayHkRedirect(_) => api_enums::PaymentMethodType::AliPayHk, Self::AmazonPayRedirect(_) => api_enums::PaymentMethodType::AmazonPay, @@ -3607,6 +3608,8 @@ pub enum WalletData { AliPayHkRedirect(AliPayHkRedirection), /// The wallet data for Amazon Pay redirect AmazonPayRedirect(AmazonPayRedirectData), + /// The wallet data for Bluecode QR Code Redirect + BluecodeRedirect {}, /// The wallet data for Skrill Skrill(SkrillData), /// The wallet data for Paysera @@ -3716,7 +3719,8 @@ impl GetAddressFromPaymentMethodData for WalletData { | Self::WeChatPayQr(_) | Self::CashappQr(_) | Self::SwishQr(_) - | Self::RevolutPay(_) => None, + | Self::RevolutPay(_) + | Self::BluecodeRedirect {} => None, } } } @@ -3903,6 +3907,9 @@ pub struct AliPayRedirection {} #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] pub struct AliPayHkRedirection {} +#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] +pub struct BluecodeQrRedirect {} + #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] pub struct MomoRedirection {} diff --git a/crates/common_enums/src/connector_enums.rs b/crates/common_enums/src/connector_enums.rs index c3741e0f5e..5bdec1152b 100644 --- a/crates/common_enums/src/connector_enums.rs +++ b/crates/common_enums/src/connector_enums.rs @@ -72,7 +72,7 @@ pub enum RoutableConnectors { Bambora, Bamboraapac, Bluesnap, - // Bluecode, + Bluecode, Boku, Braintree, Breadpay, @@ -238,7 +238,7 @@ pub enum Connector { Billwerk, Bitpay, Bluesnap, - // Bluecode, + Bluecode, Boku, Braintree, Breadpay, @@ -431,7 +431,7 @@ impl Connector { | Self::Billwerk | Self::Bitpay | Self::Bluesnap - // | Self::Bluecode + | Self::Bluecode | Self::Boku | Self::Braintree | Self::Breadpay @@ -602,7 +602,7 @@ impl From for Connector { RoutableConnectors::Bambora => Self::Bambora, RoutableConnectors::Bamboraapac => Self::Bamboraapac, RoutableConnectors::Bluesnap => Self::Bluesnap, - // RoutableConnectors::Bluecode => Self::Bluecode, + RoutableConnectors::Bluecode => Self::Bluecode, RoutableConnectors::Boku => Self::Boku, RoutableConnectors::Braintree => Self::Braintree, RoutableConnectors::Breadpay => Self::Breadpay, @@ -731,7 +731,7 @@ impl TryFrom for RoutableConnectors { Connector::Bambora => Ok(Self::Bambora), Connector::Bamboraapac => Ok(Self::Bamboraapac), Connector::Bluesnap => Ok(Self::Bluesnap), - // Connector::Bluecode => Ok(Self::Bluecode), + Connector::Bluecode => Ok(Self::Bluecode), Connector::Boku => Ok(Self::Boku), Connector::Braintree => Ok(Self::Braintree), Connector::Breadpay => Ok(Self::Breadpay), diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 33e0e5da86..99b8c2e600 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -1940,6 +1940,7 @@ pub enum PaymentMethodType { Benefit, Bizum, Blik, + Bluecode, Boleto, BcaBankTransfer, BniVa, @@ -2062,6 +2063,7 @@ impl PaymentMethodType { Self::Benefit => "Benefit", Self::Bizum => "Bizum", Self::Blik => "BLIK", + Self::Bluecode => "Bluecode", Self::Boleto => "Boleto Bancário", Self::BcaBankTransfer => "BCA Bank Transfer", Self::BniVa => "BNI Virtual Account", diff --git a/crates/common_enums/src/transformers.rs b/crates/common_enums/src/transformers.rs index 3e5c50f220..8d1549b80a 100644 --- a/crates/common_enums/src/transformers.rs +++ b/crates/common_enums/src/transformers.rs @@ -1815,6 +1815,7 @@ impl From for PaymentMethod { PaymentMethodType::Benefit => Self::CardRedirect, PaymentMethodType::Bizum => Self::BankRedirect, PaymentMethodType::Blik => Self::BankRedirect, + PaymentMethodType::Bluecode => Self::Wallet, PaymentMethodType::Alfamart => Self::Voucher, PaymentMethodType::CardRedirect => Self::CardRedirect, PaymentMethodType::CimbVa => Self::BankTransfer, diff --git a/crates/connector_configs/src/connector.rs b/crates/connector_configs/src/connector.rs index 84e7398f61..c1d42c4fef 100644 --- a/crates/connector_configs/src/connector.rs +++ b/crates/connector_configs/src/connector.rs @@ -392,6 +392,7 @@ impl ConnectorConfig { Connector::Billwerk => Ok(connector_data.billwerk), Connector::Bitpay => Ok(connector_data.bitpay), Connector::Bluesnap => Ok(connector_data.bluesnap), + Connector::Bluecode => Ok(connector_data.bluecode), Connector::Boku => Ok(connector_data.boku), Connector::Braintree => Ok(connector_data.braintree), Connector::Breadpay => Ok(connector_data.breadpay), diff --git a/crates/connector_configs/toml/development.toml b/crates/connector_configs/toml/development.toml index 4942bc6af7..ab2ebb4725 100644 --- a/crates/connector_configs/toml/development.toml +++ b/crates/connector_configs/toml/development.toml @@ -6479,8 +6479,18 @@ api_key="Signing key" api_secret="website name" key1="merchant_id" [bluecode] +[[bluecode.wallet]] + payment_method_type = "bluecode" [bluecode.connector_auth.HeaderKey] -api_key = "API Key" +api_key = "E-Order Token" +[bluecode.metadata.shop_name] +name="shop_name" +label="Shop Name" +placeholder="Enter your Shop Name" +required=true +type="Text" +[bluecode.connector_webhook_details] +merchant_secret="Source verification key" [katapult] [katapult.connector_auth.HeaderKey] diff --git a/crates/connector_configs/toml/production.toml b/crates/connector_configs/toml/production.toml index 90053b8dc3..0b378a0571 100644 --- a/crates/connector_configs/toml/production.toml +++ b/crates/connector_configs/toml/production.toml @@ -5099,9 +5099,20 @@ key1="merchant_id" [mpgs] [mpgs.connector_auth.HeaderKey] api_key = "API Key" + [bluecode] +[[bluecode.wallet]] + payment_method_type = "bluecode" [bluecode.connector_auth.HeaderKey] -api_key = "API Key" +api_key = "E-Order Token" +[bluecode.metadata.shop_name] +name="shop_name" +label="Shop Name" +placeholder="Enter your Shop Name" +required=true +type="Text" +[bluecode.connector_webhook_details] +merchant_secret="Source verification key" [katapult] [katapult.connector_auth.HeaderKey] diff --git a/crates/connector_configs/toml/sandbox.toml b/crates/connector_configs/toml/sandbox.toml index cf0ea2a906..957e966e1f 100644 --- a/crates/connector_configs/toml/sandbox.toml +++ b/crates/connector_configs/toml/sandbox.toml @@ -6460,8 +6460,18 @@ api_key="Signing key" api_secret="website name" key1="merchant_id" [bluecode] +[[bluecode.wallet]] + payment_method_type = "bluecode" [bluecode.connector_auth.HeaderKey] -api_key = "API Key" +api_key = "E-Order Token" +[bluecode.metadata.shop_name] +name="shop_name" +label="Shop Name" +placeholder="Enter your Shop Name" +required=true +type="Text" +[bluecode.connector_webhook_details] +merchant_secret="Source verification key" [katapult] [katapult.connector_auth.HeaderKey] diff --git a/crates/euclid/src/frontend/dir/enums.rs b/crates/euclid/src/frontend/dir/enums.rs index af30a30a3e..0cc056a963 100644 --- a/crates/euclid/src/frontend/dir/enums.rs +++ b/crates/euclid/src/frontend/dir/enums.rs @@ -76,6 +76,7 @@ pub enum PayLaterType { #[serde(rename_all = "snake_case")] #[strum(serialize_all = "snake_case")] pub enum WalletType { + Bluecode, GooglePay, AmazonPay, Skrill, diff --git a/crates/euclid/src/frontend/dir/lowering.rs b/crates/euclid/src/frontend/dir/lowering.rs index 3bd8dda085..b2853fbd53 100644 --- a/crates/euclid/src/frontend/dir/lowering.rs +++ b/crates/euclid/src/frontend/dir/lowering.rs @@ -41,6 +41,7 @@ impl From for global_enums::PaymentMethodType { impl From for global_enums::PaymentMethodType { fn from(value: enums::WalletType) -> Self { match value { + enums::WalletType::Bluecode => Self::Bluecode, enums::WalletType::GooglePay => Self::GooglePay, enums::WalletType::AmazonPay => Self::AmazonPay, enums::WalletType::Skrill => Self::Skrill, diff --git a/crates/euclid/src/frontend/dir/transformers.rs b/crates/euclid/src/frontend/dir/transformers.rs index d707b8ef5b..0f0e8b96aa 100644 --- a/crates/euclid/src/frontend/dir/transformers.rs +++ b/crates/euclid/src/frontend/dir/transformers.rs @@ -25,6 +25,7 @@ impl IntoDirValue for (global_enums::PaymentMethodType, global_enums::PaymentMet global_enums::PaymentMethodType::Skrill => Ok(dirval!(WalletType = Skrill)), global_enums::PaymentMethodType::Paysera => Ok(dirval!(WalletType = Paysera)), global_enums::PaymentMethodType::GooglePay => Ok(dirval!(WalletType = GooglePay)), + global_enums::PaymentMethodType::Bluecode => Ok(dirval!(WalletType = Bluecode)), global_enums::PaymentMethodType::ApplePay => Ok(dirval!(WalletType = ApplePay)), global_enums::PaymentMethodType::Paypal => Ok(dirval!(WalletType = Paypal)), global_enums::PaymentMethodType::RevolutPay => Ok(dirval!(WalletType = RevolutPay)), diff --git a/crates/hyperswitch_connectors/src/connectors/aci/transformers.rs b/crates/hyperswitch_connectors/src/connectors/aci/transformers.rs index 033563d54c..887daeee98 100644 --- a/crates/hyperswitch_connectors/src/connectors/aci/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/aci/transformers.rs @@ -153,6 +153,7 @@ impl TryFrom<(&WalletData, &PaymentsAuthorizeRouterData)> for PaymentDetails { | WalletData::ApplePayThirdPartySdk(_) | WalletData::DanaRedirect { .. } | WalletData::GooglePay(_) + | WalletData::BluecodeRedirect {} | WalletData::GooglePayThirdPartySdk(_) | WalletData::MobilePayRedirect(_) | WalletData::PaypalRedirect(_) diff --git a/crates/hyperswitch_connectors/src/connectors/adyen.rs b/crates/hyperswitch_connectors/src/connectors/adyen.rs index ce9f42f933..302c33f650 100644 --- a/crates/hyperswitch_connectors/src/connectors/adyen.rs +++ b/crates/hyperswitch_connectors/src/connectors/adyen.rs @@ -341,7 +341,8 @@ impl ConnectorValidation for Adyen { | PaymentMethodType::IndonesianBankTransfer | PaymentMethodType::SepaBankTransfer | PaymentMethodType::Flexiti - | PaymentMethodType::RevolutPay => { + | PaymentMethodType::RevolutPay + | PaymentMethodType::Bluecode => { capture_method_not_supported!(connector, capture_method, payment_method_type) } }, diff --git a/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs b/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs index 18f120c7c5..3d04375b10 100644 --- a/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs @@ -2307,6 +2307,7 @@ impl TryFrom<(&WalletData, &PaymentsAuthorizeRouterData)> for AdyenPaymentMethod | WalletData::ApplePayThirdPartySdk(_) | WalletData::GooglePayRedirect(_) | WalletData::GooglePayThirdPartySdk(_) + | WalletData::BluecodeRedirect {} | WalletData::PaypalSdk(_) | WalletData::WeChatPayQr(_) | WalletData::CashappQr(_) diff --git a/crates/hyperswitch_connectors/src/connectors/airwallex/transformers.rs b/crates/hyperswitch_connectors/src/connectors/airwallex/transformers.rs index 755e567de2..288c627c1d 100644 --- a/crates/hyperswitch_connectors/src/connectors/airwallex/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/airwallex/transformers.rs @@ -812,6 +812,7 @@ fn get_wallet_details( | WalletData::GoPayRedirect(_) | WalletData::GcashRedirect(_) | WalletData::ApplePay(_) + | WalletData::BluecodeRedirect {} | WalletData::ApplePayRedirect(_) | WalletData::ApplePayThirdPartySdk(_) | WalletData::DanaRedirect {} diff --git a/crates/hyperswitch_connectors/src/connectors/authorizedotnet/transformers.rs b/crates/hyperswitch_connectors/src/connectors/authorizedotnet/transformers.rs index cf712749b5..4f8486d186 100644 --- a/crates/hyperswitch_connectors/src/connectors/authorizedotnet/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/authorizedotnet/transformers.rs @@ -557,6 +557,7 @@ impl TryFrom<&SetupMandateRouterData> for CreateCustomerProfileRequest { | WalletData::AliPayHkRedirect(_) | WalletData::AmazonPayRedirect(_) | WalletData::Paysera(_) + | WalletData::BluecodeRedirect {} | WalletData::Skrill(_) | WalletData::MomoRedirect(_) | WalletData::KakaoPayRedirect(_) @@ -2122,6 +2123,7 @@ fn get_wallet_data( | WalletData::AmazonPayRedirect(_) | WalletData::Paysera(_) | WalletData::Skrill(_) + | WalletData::BluecodeRedirect {} | WalletData::MomoRedirect(_) | WalletData::KakaoPayRedirect(_) | WalletData::GoPayRedirect(_) diff --git a/crates/hyperswitch_connectors/src/connectors/bankofamerica/transformers.rs b/crates/hyperswitch_connectors/src/connectors/bankofamerica/transformers.rs index f7d3b57baa..5f3bcdf9d3 100644 --- a/crates/hyperswitch_connectors/src/connectors/bankofamerica/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/bankofamerica/transformers.rs @@ -307,6 +307,7 @@ impl TryFrom<&SetupMandateRouterData> for BankOfAmericaPaymentsRequest { | WalletData::AmazonPayRedirect(_) | WalletData::Paysera(_) | WalletData::Skrill(_) + | WalletData::BluecodeRedirect {} | WalletData::MomoRedirect(_) | WalletData::KakaoPayRedirect(_) | WalletData::GoPayRedirect(_) @@ -1093,6 +1094,7 @@ impl TryFrom<&BankOfAmericaRouterData<&PaymentsAuthorizeRouterData>> | WalletData::AmazonPayRedirect(_) | WalletData::Paysera(_) | WalletData::Skrill(_) + | WalletData::BluecodeRedirect {} | WalletData::MomoRedirect(_) | WalletData::KakaoPayRedirect(_) | WalletData::GoPayRedirect(_) diff --git a/crates/hyperswitch_connectors/src/connectors/bluecode.rs b/crates/hyperswitch_connectors/src/connectors/bluecode.rs index 58c5b5cfa9..068ce7ae5f 100644 --- a/crates/hyperswitch_connectors/src/connectors/bluecode.rs +++ b/crates/hyperswitch_connectors/src/connectors/bluecode.rs @@ -4,12 +4,13 @@ use std::sync::LazyLock; use common_enums::enums; use common_utils::{ + crypto, errors::CustomResult, ext_traits::BytesExt, request::{Method, Request, RequestBuilder, RequestContent}, types::{AmountConvertor, FloatMajorUnit, FloatMajorUnitForConnector}, }; -use error_stack::{report, ResultExt}; +use error_stack::ResultExt; use hyperswitch_domain_models::{ payment_method_data::PaymentMethodData, router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData}, @@ -24,7 +25,8 @@ use hyperswitch_domain_models::{ RefundsData, SetupMandateRequestData, }, router_response_types::{ - ConnectorInfo, PaymentsResponseData, RefundsResponseData, SupportedPaymentMethods, + ConnectorInfo, PaymentMethodDetails, PaymentsResponseData, RefundsResponseData, + SupportedPaymentMethods, SupportedPaymentMethodsExt, }, types::{ PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData, @@ -37,16 +39,19 @@ use hyperswitch_interfaces::{ ConnectorValidation, }, configs::Connectors, - errors, + consts, errors, events::connector_api_logs::ConnectorEvent, types::{self, Response}, webhooks, }; -use masking::{ExposeInterface, Mask}; +use masking::{ExposeInterface, Mask, Secret}; +use ring::hmac; +use serde_json::Value; use transformers as bluecode; use crate::{constants::headers, types::ResponseRouterData, utils}; +const BLUECODE_API_VERSION: &str = "v1"; #[derive(Clone)] pub struct Bluecode { amount_converter: &'static (dyn AmountConvertor + Sync), @@ -105,9 +110,6 @@ impl ConnectorCommon for Bluecode { fn get_currency_unit(&self) -> api::CurrencyUnit { api::CurrencyUnit::Base - // TODO! Check connector documentation, on which unit they are processing the currency. - // If the connector accepts amount in lower unit ( i.e cents for USD) then return api::CurrencyUnit::Minor, - // if connector accepts amount in base unit (i.e dollars for USD) then return api::CurrencyUnit::Base } fn common_get_content_type(&self) -> &'static str { @@ -126,7 +128,7 @@ impl ConnectorCommon for Bluecode { .change_context(errors::ConnectorError::FailedToObtainAuthType)?; Ok(vec![( headers::AUTHORIZATION.to_string(), - auth.api_key.expose().into_masked(), + format!("token {}", auth.api_key.expose()).into_masked(), )]) } @@ -145,9 +147,9 @@ impl ConnectorCommon for Bluecode { Ok(ErrorResponse { status_code: res.status_code, - code: response.code, - message: response.message, - reason: response.reason, + code: consts::NO_ERROR_CODE.to_string(), + message: response.message.clone(), + reason: Some(response.message), attempt_status: None, connector_transaction_id: None, network_advice_code: None, @@ -183,9 +185,7 @@ impl ConnectorValidation for Bluecode { } } -impl ConnectorIntegration for Bluecode { - //TODO: implement sessions flow -} +impl ConnectorIntegration for Bluecode {} impl ConnectorIntegration for Bluecode {} @@ -210,9 +210,13 @@ impl ConnectorIntegration CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + Ok(format!( + "{}api/{}/order/payin/start", + self.base_url(connectors), + BLUECODE_API_VERSION + )) } fn get_request_body( @@ -264,12 +268,27 @@ impl ConnectorIntegration for Blu fn get_url( &self, - _req: &PaymentsSyncRouterData, - _connectors: &Connectors, + req: &PaymentsSyncRouterData, + connectors: &Connectors, ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + let connector_transaction_id = req + .request + .connector_transaction_id + .get_connector_transaction_id() + .change_context(errors::ConnectorError::MissingConnectorTransactionID)?; + + Ok(format!( + "{}api/{}/order/{}/status", + self.base_url(connectors), + BLUECODE_API_VERSION, + connector_transaction_id + )) } fn build_request( @@ -323,17 +353,32 @@ impl ConnectorIntegration for Blu event_builder: Option<&mut ConnectorEvent>, res: Response, ) -> CustomResult { - let response: bluecode::BluecodePaymentsResponse = res + let response: bluecode::BluecodeSyncResponse = res .response .parse_struct("bluecode PaymentsSyncResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); - RouterData::try_from(ResponseRouterData { + + let response_integrity_object = utils::get_sync_integrity_object( + self.amount_converter, + response.amount, + response.currency.to_string(), + )?; + + let new_router_data = RouterData::try_from(ResponseRouterData { response, data: data.clone(), http_code: res.status_code, - }) + }); + + new_router_data + .change_context(errors::ConnectorError::ResponseHandlingFailed) + .map(|mut router_data| { + router_data.request.integrity_object = Some(response_integrity_object); + router_data + }) } fn get_error_response( @@ -363,7 +408,11 @@ impl ConnectorIntegration fo _req: &PaymentsCaptureRouterData, _connectors: &Connectors, ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + Err(errors::ConnectorError::FlowNotSupported { + flow: "Capture".to_string(), + connector: "Bluecode".to_string(), + } + .into()) } fn get_request_body( @@ -371,7 +420,11 @@ impl ConnectorIntegration fo _req: &PaymentsCaptureRouterData, _connectors: &Connectors, ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into()) + Err(errors::ConnectorError::FlowNotSupported { + flow: "Capture".to_string(), + connector: "Bluecode".to_string(), + } + .into()) } fn build_request( @@ -442,23 +495,23 @@ impl ConnectorIntegration for Bluecod _req: &RefundsRouterData, _connectors: &Connectors, ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + Err(errors::ConnectorError::FlowNotSupported { + flow: "Refund".to_string(), + connector: "Bluecode".to_string(), + } + .into()) } fn get_request_body( &self, - req: &RefundsRouterData, + _req: &RefundsRouterData, _connectors: &Connectors, ) -> CustomResult { - let refund_amount = utils::convert_amount( - self.amount_converter, - req.request.minor_refund_amount, - req.request.currency, - )?; - - let connector_router_data = bluecode::BluecodeRouterData::from((refund_amount, req)); - let connector_req = bluecode::BluecodeRefundRequest::try_from(&connector_router_data)?; - Ok(RequestContent::Json(Box::new(connector_req))) + Err(errors::ConnectorError::FlowNotSupported { + flow: "Refund".to_string(), + connector: "Bluecode".to_string(), + } + .into()) } fn build_request( @@ -526,25 +579,23 @@ impl ConnectorIntegration for Bluecode _req: &RefundSyncRouterData, _connectors: &Connectors, ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + Err(errors::ConnectorError::FlowNotSupported { + flow: "RSync".to_string(), + connector: "Bluecode".to_string(), + } + .into()) } fn build_request( &self, - req: &RefundSyncRouterData, - connectors: &Connectors, + _req: &RefundSyncRouterData, + _connectors: &Connectors, ) -> CustomResult, errors::ConnectorError> { - Ok(Some( - RequestBuilder::new() - .method(Method::Get) - .url(&types::RefundSyncType::get_url(self, req, connectors)?) - .attach_default_headers() - .headers(types::RefundSyncType::get_headers(self, req, connectors)?) - .set_body(types::RefundSyncType::get_request_body( - self, req, connectors, - )?) - .build(), - )) + Err(errors::ConnectorError::FlowNotSupported { + flow: "RSync".to_string(), + connector: "Bluecode".to_string(), + } + .into()) } fn handle_response( @@ -577,38 +628,130 @@ impl ConnectorIntegration for Bluecode #[async_trait::async_trait] impl webhooks::IncomingWebhook for Bluecode { - fn get_webhook_object_reference_id( + fn get_webhook_source_verification_algorithm( &self, _request: &webhooks::IncomingWebhookRequestDetails<'_>, + ) -> CustomResult, errors::ConnectorError> { + Ok(Box::new(crypto::HmacSha512)) + } + + fn get_webhook_source_verification_signature( + &self, + request: &webhooks::IncomingWebhookRequestDetails<'_>, + _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets, + ) -> CustomResult, errors::ConnectorError> { + let security_header = request + .headers + .get("x-eorder-webhook-signature") + .map(|header_value| { + header_value + .to_str() + .map(String::from) + .map_err(|_| errors::ConnectorError::WebhookSignatureNotFound) + }) + .ok_or(errors::ConnectorError::WebhookSignatureNotFound)??; + hex::decode(security_header) + .change_context(errors::ConnectorError::WebhookSignatureNotFound) + } + + async fn verify_webhook_source( + &self, + request: &webhooks::IncomingWebhookRequestDetails<'_>, + merchant_id: &common_utils::id_type::MerchantId, + connector_webhook_details: Option, + _connector_account_details: crypto::Encryptable>, + connector_name: &str, + ) -> CustomResult { + let connector_webhook_secrets = self + .get_webhook_source_verification_merchant_secret( + merchant_id, + connector_name, + connector_webhook_details, + ) + .await?; + + let signature = self + .get_webhook_source_verification_signature(request, &connector_webhook_secrets) + .change_context(errors::ConnectorError::WebhookSignatureNotFound)?; + + let secret_bytes = connector_webhook_secrets.secret.as_ref(); + + let parsed: Value = serde_json::from_slice(request.body) + .map_err(|_| errors::ConnectorError::WebhookSourceVerificationFailed)?; + + let sorted_payload = bluecode::sort_and_minify_json(&parsed)?; + + let key = hmac::Key::new(hmac::HMAC_SHA512, secret_bytes); + + let verify = hmac::verify(&key, sorted_payload.as_bytes(), &signature) + .map(|_| true) + .map_err(|_| errors::ConnectorError::WebhookSourceVerificationFailed)?; + + Ok(verify) + } + + fn get_webhook_object_reference_id( + &self, + request: &webhooks::IncomingWebhookRequestDetails<'_>, ) -> CustomResult { - Err(report!(errors::ConnectorError::WebhooksNotImplemented)) + let webhook_body = transformers::get_webhook_object_from_body(request.body) + .change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?; + + Ok(api_models::webhooks::ObjectReferenceId::PaymentId( + api_models::payments::PaymentIdType::ConnectorTransactionId(webhook_body.order_id), + )) } fn get_webhook_event_type( &self, - _request: &webhooks::IncomingWebhookRequestDetails<'_>, + request: &webhooks::IncomingWebhookRequestDetails<'_>, ) -> CustomResult { - Err(report!(errors::ConnectorError::WebhooksNotImplemented)) + let webhook_body = transformers::get_webhook_object_from_body(request.body) + .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?; + + Ok(transformers::get_bluecode_webhook_event( + webhook_body.status, + )) } fn get_webhook_resource_object( &self, - _request: &webhooks::IncomingWebhookRequestDetails<'_>, + request: &webhooks::IncomingWebhookRequestDetails<'_>, ) -> CustomResult, errors::ConnectorError> { - Err(report!(errors::ConnectorError::WebhooksNotImplemented)) + let webhook_body = transformers::get_webhook_object_from_body(request.body) + .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?; + + Ok(Box::new(webhook_body)) } } static BLUECODE_SUPPORTED_PAYMENT_METHODS: LazyLock = - LazyLock::new(SupportedPaymentMethods::new); + LazyLock::new(|| { + let supported_capture_methods = vec![enums::CaptureMethod::Automatic]; + + let mut santander_supported_payment_methods = SupportedPaymentMethods::new(); + + santander_supported_payment_methods.add( + enums::PaymentMethod::Wallet, + enums::PaymentMethodType::Bluecode, + PaymentMethodDetails { + mandates: enums::FeatureStatus::NotSupported, + refunds: enums::FeatureStatus::NotSupported, + supported_capture_methods, + specific_features: None, + }, + ); + + santander_supported_payment_methods + }); static BLUECODE_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo { display_name: "Bluecode", - description: "Bluecode connector", - connector_type: enums::PaymentConnectorCategory::PaymentGateway, + description: "Bluecode is building a global payment network that combines Alipay+, Discover and EMPSA and enables seamless payments in 75 countries. With over 160 million acceptance points, payments are processed according to the highest European security and data protection standards to make Europe less dependent on international players.", + connector_type: enums::PaymentConnectorCategory::AlternativePaymentMethod, }; -static BLUECODE_SUPPORTED_WEBHOOK_FLOWS: [enums::EventClass; 0] = []; +static BLUECODE_SUPPORTED_WEBHOOK_FLOWS: [enums::EventClass; 1] = [enums::EventClass::Payments]; impl ConnectorSpecifications for Bluecode { fn get_connector_about(&self) -> Option<&'static ConnectorInfo> { diff --git a/crates/hyperswitch_connectors/src/connectors/bluecode/transformers.rs b/crates/hyperswitch_connectors/src/connectors/bluecode/transformers.rs index 6841510f8b..2bcecbe13c 100644 --- a/crates/hyperswitch_connectors/src/connectors/bluecode/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/bluecode/transformers.rs @@ -1,28 +1,39 @@ +use std::collections::HashMap; + use common_enums::enums; -use common_utils::types::FloatMajorUnit; +use common_utils::{ + errors::CustomResult, + ext_traits::ByteSliceExt, + pii::{Email, IpAddress}, + request::Method, + types::FloatMajorUnit, +}; +use error_stack::ResultExt; use hyperswitch_domain_models::{ - payment_method_data::PaymentMethodData, + payment_method_data::{PaymentMethodData, WalletData}, router_data::{ConnectorAuthType, RouterData}, router_flow_types::refunds::{Execute, RSync}, router_request_types::ResponseId, - router_response_types::{PaymentsResponseData, RefundsResponseData}, + router_response_types::{PaymentsResponseData, RedirectForm, RefundsResponseData}, types::{PaymentsAuthorizeRouterData, RefundsRouterData}, }; use hyperswitch_interfaces::errors; use masking::Secret; use serde::{Deserialize, Serialize}; +use serde_json::{Map, Value}; -use crate::types::{RefundsResponseRouterData, ResponseRouterData}; +use crate::{ + types::{RefundsResponseRouterData, ResponseRouterData}, + utils::{self as connector_utils, PaymentsAuthorizeRequestData, RouterData as OtherRouterData}, +}; -//TODO: Fill the struct with respective fields pub struct BluecodeRouterData { - pub amount: FloatMajorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc. + pub amount: FloatMajorUnit, pub router_data: T, } impl From<(FloatMajorUnit, T)> for BluecodeRouterData { fn from((amount, item): (FloatMajorUnit, T)) -> Self { - //Todo : use utils to convert the amount to the type of amount that a connector accepts Self { amount, router_data: item, @@ -30,11 +41,41 @@ impl From<(FloatMajorUnit, T)> for BluecodeRouterData { } } -//TODO: Fill the struct with respective fields -#[derive(Default, Debug, Serialize, PartialEq)] +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct BluecodeMetadataObject { + pub shop_name: String, +} + +impl TryFrom<&Option> for BluecodeMetadataObject { + type Error = error_stack::Report; + fn try_from( + meta_data: &Option, + ) -> Result { + let metadata = connector_utils::to_connector_meta_from_secret::(meta_data.clone()) + .change_context(errors::ConnectorError::InvalidConnectorConfig { + config: "metadata", + })?; + Ok(metadata) + } +} + +#[derive(Debug, Serialize, PartialEq)] pub struct BluecodePaymentsRequest { - amount: FloatMajorUnit, - card: BluecodeCard, + pub amount: FloatMajorUnit, + pub currency: enums::Currency, + pub payment_provider: String, + pub shop_name: String, + pub reference: String, + pub ip_address: Option>, + pub first_name: Secret, + pub last_name: Secret, + pub billing_address_country_code_iso: enums::CountryAlpha2, + pub billing_address_city: String, + pub billing_address_line1: Secret, + pub billing_address_postal_code: Secret, + pub webhook_url: String, + pub success_url: String, + pub failure_url: String, } #[derive(Default, Debug, Serialize, Eq, PartialEq)] @@ -51,18 +92,60 @@ impl TryFrom<&BluecodeRouterData<&PaymentsAuthorizeRouterData>> for BluecodePaym fn try_from( item: &BluecodeRouterData<&PaymentsAuthorizeRouterData>, ) -> Result { + if item.router_data.request.capture_method != Some(enums::CaptureMethod::Automatic) { + return Err(errors::ConnectorError::FlowNotSupported { + flow: format!("{:?}", item.router_data.request.capture_method), + connector: "Bluecode".to_string(), + } + .into()); + } match item.router_data.request.payment_method_data.clone() { - PaymentMethodData::Card(_) => Err(errors::ConnectorError::NotImplemented( - "Card payment method not implemented".to_string(), - ) - .into()), + PaymentMethodData::Wallet(WalletData::BluecodeRedirect {}) => { + let bluecode_mca_metadata = + BluecodeMetadataObject::try_from(&item.router_data.connector_meta_data)?; + Self::try_from((item, &bluecode_mca_metadata)) + } _ => Err(errors::ConnectorError::NotImplemented("Payment method".to_string()).into()), } } } -//TODO: Fill the struct with respective fields -// Auth Struct +impl + TryFrom<( + &BluecodeRouterData<&PaymentsAuthorizeRouterData>, + &BluecodeMetadataObject, + )> for BluecodePaymentsRequest +{ + type Error = error_stack::Report; + + fn try_from( + value: ( + &BluecodeRouterData<&PaymentsAuthorizeRouterData>, + &BluecodeMetadataObject, + ), + ) -> Result { + let item = value.0; + + Ok(Self { + amount: item.amount, + currency: item.router_data.request.currency, + payment_provider: "bluecode_payment".to_string(), + shop_name: value.1.shop_name.clone(), + reference: item.router_data.payment_id.clone(), + ip_address: item.router_data.request.get_ip_address_as_optional(), + first_name: item.router_data.get_billing_first_name()?, + last_name: item.router_data.get_billing_last_name()?, + billing_address_country_code_iso: item.router_data.get_billing_country()?, + billing_address_city: item.router_data.get_billing_city()?, + billing_address_line1: item.router_data.get_billing_line1()?, + billing_address_postal_code: item.router_data.get_billing_zip()?, + webhook_url: item.router_data.request.get_webhook_url()?, + success_url: item.router_data.request.get_router_return_url()?, + failure_url: item.router_data.request.get_router_return_url()?, + }) + } +} + pub struct BluecodeAuthType { pub(super) api_key: Secret, } @@ -78,32 +161,90 @@ impl TryFrom<&ConnectorAuthType> for BluecodeAuthType { } } } -// PaymentsResponse -//TODO: Append the remaining status flags -#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq)] -#[serde(rename_all = "lowercase")] -pub enum BluecodePaymentStatus { - Succeeded, - Failed, - #[default] - Processing, -} impl From for common_enums::AttemptStatus { fn from(item: BluecodePaymentStatus) -> Self { match item { - BluecodePaymentStatus::Succeeded => Self::Charged, + BluecodePaymentStatus::ManualProcessing => Self::Pending, + BluecodePaymentStatus::Pending | BluecodePaymentStatus::PaymentInitiated => { + Self::AuthenticationPending + } BluecodePaymentStatus::Failed => Self::Failure, - BluecodePaymentStatus::Processing => Self::Authorizing, + BluecodePaymentStatus::Completed => Self::Charged, } } } -//TODO: Fill the struct with respective fields -#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct BluecodePaymentsResponse { - status: BluecodePaymentStatus, - id: String, + pub id: i64, + pub order_id: String, + pub amount: FloatMajorUnit, + pub currency: enums::Currency, + pub charged_amount: FloatMajorUnit, + pub charged_currency: enums::Currency, + pub status: BluecodePaymentStatus, + pub payment_link: url::Url, + pub etoken: Secret, + pub payment_request_id: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct BluecodeSyncResponse { + pub id: Option, + pub order_id: String, + pub user_id: Option, + pub customer_id: Option, + pub customer_email: Option, + pub customer_phone: Option, + pub status: BluecodePaymentStatus, + pub payment_provider: Option, + pub payment_connector: Option, + pub payment_method: Option, + pub payment_method_type: Option, + pub shop_name: Option, + pub sender_name: Option, + pub sender_email: Option, + pub description: Option, + pub amount: FloatMajorUnit, + pub currency: enums::Currency, + pub charged_amount: Option, + pub charged_amount_currency: Option, + pub charged_fx_amount: Option, + pub charged_fx_amount_currency: Option, + pub is_underpaid: Option, + pub billing_amount: Option, + pub billing_currency: Option, + pub language: Option, + pub ip_address: Option>, + pub first_name: Option>, + pub last_name: Option>, + pub billing_address_line1: Option>, + pub billing_address_city: Option>, + pub billing_address_postal_code: Option>, + pub billing_address_country: Option, + pub billing_address_country_code_iso: Option, + pub shipping_address_country_code_iso: Option, + pub success_url: Option, + pub failure_url: Option, + pub source: Option, + pub bonus_code: Option, + pub dob: Option, + pub fees_amount: Option, + pub fx_margin_amount: Option, + pub fx_margin_percent: Option, + pub fees_percent: Option, + pub reseller_id: Option, +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum BluecodePaymentStatus { + Pending, + PaymentInitiated, + ManualProcessing, + Failed, + Completed, } impl TryFrom> @@ -113,15 +254,17 @@ impl TryFrom, ) -> Result { + let url = item.response.payment_link.clone(); + let redirection_data = Some(RedirectForm::from((url, Method::Get))); Ok(Self { status: common_enums::AttemptStatus::from(item.response.status), response: Ok(PaymentsResponseData::TransactionResponse { - resource_id: ResponseId::ConnectorTransactionId(item.response.id), - redirection_data: Box::new(None), + resource_id: ResponseId::ConnectorTransactionId(item.response.order_id), + redirection_data: Box::new(redirection_data), mandate_reference: Box::new(None), connector_metadata: None, network_txn_id: None, - connector_response_reference_id: None, + connector_response_reference_id: Some(item.response.payment_request_id), incremental_authorization_allowed: None, charges: None, }), @@ -130,9 +273,21 @@ impl TryFrom TryFrom> + for RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: ResponseRouterData, + ) -> Result { + Ok(Self { + status: common_enums::AttemptStatus::from(item.response.status), + response: item.data.response, + ..item.data + }) + } +} + #[derive(Default, Debug, Serialize)] pub struct BluecodeRefundRequest { pub amount: FloatMajorUnit, @@ -206,14 +361,58 @@ impl TryFrom> for RefundsRouter } } -//TODO: Fill the struct with respective fields #[derive(Default, Debug, Serialize, Deserialize, PartialEq)] pub struct BluecodeErrorResponse { - pub status_code: u16, - pub code: String, pub message: String, - pub reason: Option, - pub network_advice_code: Option, - pub network_decline_code: Option, - pub network_error_message: Option, + pub context_data: HashMap, +} + +pub(crate) fn get_bluecode_webhook_event( + status: BluecodePaymentStatus, +) -> api_models::webhooks::IncomingWebhookEvent { + match status { + BluecodePaymentStatus::Completed => { + api_models::webhooks::IncomingWebhookEvent::PaymentIntentSuccess + } + BluecodePaymentStatus::PaymentInitiated + | BluecodePaymentStatus::ManualProcessing + | BluecodePaymentStatus::Pending => { + api_models::webhooks::IncomingWebhookEvent::PaymentIntentProcessing + } + BluecodePaymentStatus::Failed => { + api_models::webhooks::IncomingWebhookEvent::PaymentIntentFailure + } + } +} + +pub(crate) fn get_webhook_object_from_body( + body: &[u8], +) -> CustomResult { + let webhook: BluecodeSyncResponse = body.parse_struct("BluecodeIncomingWebhook")?; + + Ok(webhook) +} + +pub fn sort_and_minify_json(value: &Value) -> Result { + fn sort_value(val: &Value) -> Value { + match val { + Value::Object(map) => { + let mut entries: Vec<_> = map.iter().collect(); + entries.sort_by_key(|(k, _)| k.to_owned()); + + let sorted_map: Map = entries + .into_iter() + .map(|(k, v)| (k.clone(), sort_value(v))) + .collect(); + + Value::Object(sorted_map) + } + Value::Array(arr) => Value::Array(arr.iter().map(sort_value).collect()), + _ => val.clone(), + } + } + + let sorted_value = sort_value(value); + serde_json::to_string(&sorted_value) + .map_err(|_| errors::ConnectorError::WebhookBodyDecodingFailed) } diff --git a/crates/hyperswitch_connectors/src/connectors/bluesnap/transformers.rs b/crates/hyperswitch_connectors/src/connectors/bluesnap/transformers.rs index afa53bc03a..484a06565f 100644 --- a/crates/hyperswitch_connectors/src/connectors/bluesnap/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/bluesnap/transformers.rs @@ -371,6 +371,7 @@ impl TryFrom<&BluesnapRouterData<&types::PaymentsAuthorizeRouterData>> for Blues | WalletData::AmazonPayRedirect(_) | WalletData::Paysera(_) | WalletData::Skrill(_) + | WalletData::BluecodeRedirect {} | WalletData::MomoRedirect(_) | WalletData::KakaoPayRedirect(_) | WalletData::GoPayRedirect(_) diff --git a/crates/hyperswitch_connectors/src/connectors/boku/transformers.rs b/crates/hyperswitch_connectors/src/connectors/boku/transformers.rs index 10e47e2821..2763e5a5e4 100644 --- a/crates/hyperswitch_connectors/src/connectors/boku/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/boku/transformers.rs @@ -183,6 +183,7 @@ fn get_wallet_type(wallet_data: &WalletData) -> Result for TokenRequest { | WalletData::AmazonPayRedirect(_) | WalletData::Paysera(_) | WalletData::Skrill(_) + | WalletData::BluecodeRedirect {} | WalletData::MomoRedirect(_) | WalletData::KakaoPayRedirect(_) | WalletData::GoPayRedirect(_) @@ -368,6 +369,7 @@ impl TryFrom<&CheckoutRouterData<&PaymentsAuthorizeRouterData>> for PaymentsRequ | WalletData::AmazonPayRedirect(_) | WalletData::Paysera(_) | WalletData::Skrill(_) + | WalletData::BluecodeRedirect {} | WalletData::MomoRedirect(_) | WalletData::KakaoPayRedirect(_) | WalletData::GoPayRedirect(_) diff --git a/crates/hyperswitch_connectors/src/connectors/cybersource/transformers.rs b/crates/hyperswitch_connectors/src/connectors/cybersource/transformers.rs index 2112989969..27ac86fe7f 100644 --- a/crates/hyperswitch_connectors/src/connectors/cybersource/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/cybersource/transformers.rs @@ -265,6 +265,7 @@ impl TryFrom<&SetupMandateRouterData> for CybersourceZeroMandateRequest { | WalletData::AmazonPayRedirect(_) | WalletData::Paysera(_) | WalletData::Skrill(_) + | WalletData::BluecodeRedirect {} | WalletData::MomoRedirect(_) | WalletData::KakaoPayRedirect(_) | WalletData::GoPayRedirect(_) @@ -2257,6 +2258,7 @@ impl TryFrom<&CybersourceRouterData<&PaymentsAuthorizeRouterData>> for Cybersour | WalletData::AmazonPayRedirect(_) | WalletData::Paysera(_) | WalletData::Skrill(_) + | WalletData::BluecodeRedirect {} | WalletData::MomoRedirect(_) | WalletData::KakaoPayRedirect(_) | WalletData::GoPayRedirect(_) diff --git a/crates/hyperswitch_connectors/src/connectors/fiuu/transformers.rs b/crates/hyperswitch_connectors/src/connectors/fiuu/transformers.rs index 6d34f04911..5672740c55 100644 --- a/crates/hyperswitch_connectors/src/connectors/fiuu/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/fiuu/transformers.rs @@ -567,6 +567,7 @@ impl TryFrom<&FiuuRouterData<&PaymentsAuthorizeRouterData>> for FiuuPaymentReque | WalletData::AmazonPayRedirect(_) | WalletData::Paysera(_) | WalletData::Skrill(_) + | WalletData::BluecodeRedirect {} | WalletData::MomoRedirect(_) | WalletData::KakaoPayRedirect(_) | WalletData::GoPayRedirect(_) diff --git a/crates/hyperswitch_connectors/src/connectors/globepay/transformers.rs b/crates/hyperswitch_connectors/src/connectors/globepay/transformers.rs index df427d14fa..eafb54d81b 100644 --- a/crates/hyperswitch_connectors/src/connectors/globepay/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/globepay/transformers.rs @@ -62,6 +62,7 @@ impl TryFrom<&GlobepayRouterData<&types::PaymentsAuthorizeRouterData>> for Globe | WalletData::AmazonPayRedirect(_) | WalletData::Paysera(_) | WalletData::Skrill(_) + | WalletData::BluecodeRedirect {} | WalletData::MomoRedirect(_) | WalletData::KakaoPayRedirect(_) | WalletData::GoPayRedirect(_) diff --git a/crates/hyperswitch_connectors/src/connectors/klarna.rs b/crates/hyperswitch_connectors/src/connectors/klarna.rs index adb79c08e7..7b9bdda56a 100644 --- a/crates/hyperswitch_connectors/src/connectors/klarna.rs +++ b/crates/hyperswitch_connectors/src/connectors/klarna.rs @@ -543,6 +543,7 @@ impl ConnectorIntegration> for Mifin | WalletData::AmazonPayRedirect(_) | WalletData::Paysera(_) | WalletData::Skrill(_) + | WalletData::BluecodeRedirect {} | WalletData::MomoRedirect(_) | WalletData::KakaoPayRedirect(_) | WalletData::GoPayRedirect(_) diff --git a/crates/hyperswitch_connectors/src/connectors/multisafepay/transformers.rs b/crates/hyperswitch_connectors/src/connectors/multisafepay/transformers.rs index 1c7b50153d..19c6f8e705 100644 --- a/crates/hyperswitch_connectors/src/connectors/multisafepay/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/multisafepay/transformers.rs @@ -527,6 +527,7 @@ impl TryFrom<&MultisafepayRouterData<&types::PaymentsAuthorizeRouterData>> | WalletData::AmazonPayRedirect(_) | WalletData::Paysera(_) | WalletData::Skrill(_) + | WalletData::BluecodeRedirect {} | WalletData::MomoRedirect(_) | WalletData::KakaoPayRedirect(_) | WalletData::GoPayRedirect(_) @@ -596,6 +597,7 @@ impl TryFrom<&MultisafepayRouterData<&types::PaymentsAuthorizeRouterData>> | WalletData::AmazonPayRedirect(_) | WalletData::Paysera(_) | WalletData::Skrill(_) + | WalletData::BluecodeRedirect {} | WalletData::MomoRedirect(_) | WalletData::KakaoPayRedirect(_) | WalletData::GoPayRedirect(_) @@ -766,6 +768,7 @@ impl TryFrom<&MultisafepayRouterData<&types::PaymentsAuthorizeRouterData>> | WalletData::AmazonPayRedirect(_) | WalletData::Paysera(_) | WalletData::Skrill(_) + | WalletData::BluecodeRedirect {} | WalletData::MomoRedirect(_) | WalletData::KakaoPayRedirect(_) | WalletData::GoPayRedirect(_) diff --git a/crates/hyperswitch_connectors/src/connectors/nexinets/transformers.rs b/crates/hyperswitch_connectors/src/connectors/nexinets/transformers.rs index b03cdbd024..deb9cffd10 100644 --- a/crates/hyperswitch_connectors/src/connectors/nexinets/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/nexinets/transformers.rs @@ -709,6 +709,7 @@ fn get_wallet_details( | WalletData::AmazonPayRedirect(_) | WalletData::Paysera(_) | WalletData::Skrill(_) + | WalletData::BluecodeRedirect {} | WalletData::MomoRedirect(_) | WalletData::KakaoPayRedirect(_) | WalletData::GoPayRedirect(_) diff --git a/crates/hyperswitch_connectors/src/connectors/nmi/transformers.rs b/crates/hyperswitch_connectors/src/connectors/nmi/transformers.rs index f235e55379..ec5ce3b2e6 100644 --- a/crates/hyperswitch_connectors/src/connectors/nmi/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/nmi/transformers.rs @@ -548,6 +548,7 @@ impl TryFrom<(&PaymentMethodData, Option<&PaymentsAuthorizeRouterData>)> for Pay | WalletData::AmazonPayRedirect(_) | WalletData::Paysera(_) | WalletData::Skrill(_) + | WalletData::BluecodeRedirect {} | WalletData::MomoRedirect(_) | WalletData::KakaoPayRedirect(_) | WalletData::GoPayRedirect(_) diff --git a/crates/hyperswitch_connectors/src/connectors/noon/transformers.rs b/crates/hyperswitch_connectors/src/connectors/noon/transformers.rs index 8482dd2e1d..6be0d7996c 100644 --- a/crates/hyperswitch_connectors/src/connectors/noon/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/noon/transformers.rs @@ -323,6 +323,7 @@ impl TryFrom<&NoonRouterData<&PaymentsAuthorizeRouterData>> for NoonPaymentsRequ | WalletData::AmazonPayRedirect(_) | WalletData::Paysera(_) | WalletData::Skrill(_) + | WalletData::BluecodeRedirect {} | WalletData::MomoRedirect(_) | WalletData::KakaoPayRedirect(_) | WalletData::GoPayRedirect(_) diff --git a/crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs b/crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs index 3c323f0b0d..9582171f28 100644 --- a/crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs @@ -350,6 +350,7 @@ impl TryFrom<&NovalnetRouterData<&PaymentsAuthorizeRouterData>> for NovalnetPaym | WalletDataPaymentMethod::AmazonPayRedirect(_) | WalletDataPaymentMethod::Paysera(_) | WalletDataPaymentMethod::Skrill(_) + | WalletDataPaymentMethod::BluecodeRedirect {} | WalletDataPaymentMethod::MomoRedirect(_) | WalletDataPaymentMethod::KakaoPayRedirect(_) | WalletDataPaymentMethod::GoPayRedirect(_) @@ -1604,6 +1605,7 @@ impl TryFrom<&SetupMandateRouterData> for NovalnetPaymentsRequest { | WalletDataPaymentMethod::AmazonPayRedirect(_) | WalletDataPaymentMethod::Paysera(_) | WalletDataPaymentMethod::Skrill(_) + | WalletDataPaymentMethod::BluecodeRedirect {} | WalletDataPaymentMethod::MomoRedirect(_) | WalletDataPaymentMethod::KakaoPayRedirect(_) | WalletDataPaymentMethod::GoPayRedirect(_) diff --git a/crates/hyperswitch_connectors/src/connectors/nuvei/transformers.rs b/crates/hyperswitch_connectors/src/connectors/nuvei/transformers.rs index b68ecb83f3..010109a06a 100644 --- a/crates/hyperswitch_connectors/src/connectors/nuvei/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/nuvei/transformers.rs @@ -929,6 +929,7 @@ where | WalletData::AmazonPayRedirect(_) | WalletData::Paysera(_) | WalletData::Skrill(_) + | WalletData::BluecodeRedirect {} | WalletData::MomoRedirect(_) | WalletData::KakaoPayRedirect(_) | WalletData::GoPayRedirect(_) diff --git a/crates/hyperswitch_connectors/src/connectors/payme/transformers.rs b/crates/hyperswitch_connectors/src/connectors/payme/transformers.rs index c03acfa3d4..ccd97c2e13 100644 --- a/crates/hyperswitch_connectors/src/connectors/payme/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/payme/transformers.rs @@ -400,6 +400,7 @@ impl TryFrom<&PaymentMethodData> for SalePaymentMethod { WalletData::ApplePayThirdPartySdk(_) => Ok(Self::ApplePay), WalletData::AliPayQr(_) | WalletData::AliPayRedirect(_) + | WalletData::BluecodeRedirect {} | WalletData::AliPayHkRedirect(_) | WalletData::AmazonPayRedirect(_) | WalletData::Paysera(_) diff --git a/crates/hyperswitch_connectors/src/connectors/paypal/transformers.rs b/crates/hyperswitch_connectors/src/connectors/paypal/transformers.rs index 6d93ad4cf4..7c25b7a6e9 100644 --- a/crates/hyperswitch_connectors/src/connectors/paypal/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/paypal/transformers.rs @@ -1081,6 +1081,7 @@ impl TryFrom<&PaypalRouterData<&PaymentsAuthorizeRouterData>> for PaypalPayments | WalletData::ApplePayThirdPartySdk(_) | WalletData::DanaRedirect {} | WalletData::GooglePay(_) + | WalletData::BluecodeRedirect {} | WalletData::GooglePayRedirect(_) | WalletData::GooglePayThirdPartySdk(_) | WalletData::MbWayRedirect(_) @@ -1197,6 +1198,7 @@ impl TryFrom<&PaypalRouterData<&PaymentsAuthorizeRouterData>> for PaypalPayments | enums::PaymentMethodType::Efecty | enums::PaymentMethodType::Eft | enums::PaymentMethodType::Eps + | enums::PaymentMethodType::Bluecode | enums::PaymentMethodType::Fps | enums::PaymentMethodType::Evoucher | enums::PaymentMethodType::Giropay diff --git a/crates/hyperswitch_connectors/src/connectors/shift4/transformers.rs b/crates/hyperswitch_connectors/src/connectors/shift4/transformers.rs index 2370330d17..e4c7b708b6 100644 --- a/crates/hyperswitch_connectors/src/connectors/shift4/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/shift4/transformers.rs @@ -392,6 +392,7 @@ impl TryFrom<&WalletData> for PaymentMethodType { | WalletData::GooglePayRedirect(_) | WalletData::GooglePayThirdPartySdk(_) | WalletData::GooglePay(_) + | WalletData::BluecodeRedirect {} | WalletData::PaypalRedirect(_) | WalletData::MbWayRedirect(_) | WalletData::MobilePayRedirect(_) diff --git a/crates/hyperswitch_connectors/src/connectors/square/transformers.rs b/crates/hyperswitch_connectors/src/connectors/square/transformers.rs index 76f70a5b6f..335ea265c0 100644 --- a/crates/hyperswitch_connectors/src/connectors/square/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/square/transformers.rs @@ -107,6 +107,7 @@ impl TryFrom<(&types::TokenizationRouterData, WalletData)> for SquareTokenReques match wallet_data { WalletData::ApplePay(_) | WalletData::GooglePay(_) + | WalletData::BluecodeRedirect {} | WalletData::AliPayQr(_) | WalletData::AliPayRedirect(_) | WalletData::AliPayHkRedirect(_) diff --git a/crates/hyperswitch_connectors/src/connectors/stripe/transformers.rs b/crates/hyperswitch_connectors/src/connectors/stripe/transformers.rs index 016a4f23f5..3cd7aa033d 100644 --- a/crates/hyperswitch_connectors/src/connectors/stripe/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/stripe/transformers.rs @@ -760,6 +760,7 @@ impl TryFrom for StripePaymentMethodType { | enums::PaymentMethodType::UpiCollect | enums::PaymentMethodType::UpiIntent | enums::PaymentMethodType::Cashapp + | enums::PaymentMethodType::Bluecode | enums::PaymentMethodType::Oxxo => Err(ConnectorError::NotImplemented( get_unimplemented_payment_method_error_message("stripe"), ) @@ -1126,6 +1127,7 @@ fn get_stripe_payment_method_type_from_wallet_data( )), WalletData::PaypalRedirect(_) | WalletData::AliPayQr(_) + | WalletData::BluecodeRedirect {} | WalletData::Paysera(_) | WalletData::Skrill(_) | WalletData::AliPayHkRedirect(_) @@ -1549,6 +1551,7 @@ impl TryFrom<(&WalletData, Option)> for StripePaymentMethodD ), WalletData::AliPayQr(_) | WalletData::Paysera(_) + | WalletData::BluecodeRedirect {} | WalletData::Skrill(_) | WalletData::AliPayHkRedirect(_) | WalletData::MomoRedirect(_) diff --git a/crates/hyperswitch_connectors/src/connectors/wellsfargo/transformers.rs b/crates/hyperswitch_connectors/src/connectors/wellsfargo/transformers.rs index 16730a03a0..413551a9fd 100644 --- a/crates/hyperswitch_connectors/src/connectors/wellsfargo/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/wellsfargo/transformers.rs @@ -194,6 +194,7 @@ impl TryFrom<&SetupMandateRouterData> for WellsfargoZeroMandateRequest { | WalletData::KakaoPayRedirect(_) | WalletData::GoPayRedirect(_) | WalletData::GcashRedirect(_) + | WalletData::BluecodeRedirect {} | WalletData::ApplePayRedirect(_) | WalletData::ApplePayThirdPartySdk(_) | WalletData::DanaRedirect {} @@ -1259,6 +1260,7 @@ impl TryFrom<&WellsfargoRouterData<&PaymentsAuthorizeRouterData>> for Wellsfargo | WalletData::KakaoPayRedirect(_) | WalletData::GoPayRedirect(_) | WalletData::GcashRedirect(_) + | WalletData::BluecodeRedirect {} | WalletData::ApplePayRedirect(_) | WalletData::ApplePayThirdPartySdk(_) | WalletData::DanaRedirect {} diff --git a/crates/hyperswitch_connectors/src/connectors/worldpay/transformers.rs b/crates/hyperswitch_connectors/src/connectors/worldpay/transformers.rs index 3a2fe91b23..f341ceff1f 100644 --- a/crates/hyperswitch_connectors/src/connectors/worldpay/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/worldpay/transformers.rs @@ -158,6 +158,7 @@ fn fetch_payment_instrument( | WalletData::MomoRedirect(_) | WalletData::KakaoPayRedirect(_) | WalletData::GoPayRedirect(_) + | WalletData::BluecodeRedirect {} | WalletData::GcashRedirect(_) | WalletData::ApplePayRedirect(_) | WalletData::ApplePayThirdPartySdk(_) diff --git a/crates/hyperswitch_connectors/src/connectors/zen/transformers.rs b/crates/hyperswitch_connectors/src/connectors/zen/transformers.rs index 7aa2b8d865..48aa2a51fe 100644 --- a/crates/hyperswitch_connectors/src/connectors/zen/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/zen/transformers.rs @@ -488,6 +488,7 @@ impl | WalletData::PaypalRedirect(_) | WalletData::ApplePay(_) | WalletData::GooglePay(_) + | WalletData::BluecodeRedirect {} | WalletData::AliPayQr(_) | WalletData::AliPayRedirect(_) | WalletData::AliPayHkRedirect(_) diff --git a/crates/hyperswitch_connectors/src/utils.rs b/crates/hyperswitch_connectors/src/utils.rs index 3194349883..d2ee231d14 100644 --- a/crates/hyperswitch_connectors/src/utils.rs +++ b/crates/hyperswitch_connectors/src/utils.rs @@ -5480,6 +5480,7 @@ pub enum PaymentMethodDataType { DanaRedirect, DuitNow, GooglePay, + Bluecode, GooglePayRedirect, GooglePayThirdPartySdk, MbWayRedirect, @@ -5610,6 +5611,7 @@ impl From for PaymentMethodDataType { } payment_method_data::WalletData::DanaRedirect {} => Self::DanaRedirect, payment_method_data::WalletData::GooglePay(_) => Self::GooglePay, + payment_method_data::WalletData::BluecodeRedirect {} => Self::Bluecode, payment_method_data::WalletData::GooglePayRedirect(_) => Self::GooglePayRedirect, payment_method_data::WalletData::GooglePayThirdPartySdk(_) => { Self::GooglePayThirdPartySdk diff --git a/crates/hyperswitch_domain_models/src/payment_method_data.rs b/crates/hyperswitch_domain_models/src/payment_method_data.rs index ae167b3a28..adb4e38078 100644 --- a/crates/hyperswitch_domain_models/src/payment_method_data.rs +++ b/crates/hyperswitch_domain_models/src/payment_method_data.rs @@ -252,6 +252,7 @@ pub enum WalletData { AliPayRedirect(AliPayRedirection), AliPayHkRedirect(AliPayHkRedirection), AmazonPayRedirect(Box), + BluecodeRedirect {}, Paysera(Box), Skrill(Box), MomoRedirect(MomoRedirection), @@ -404,6 +405,9 @@ pub struct AliPayHkRedirection {} #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize)] pub struct AmazonPayRedirect {} +#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize)] +pub struct BluecodeQrRedirect {} + #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize)] pub struct PayseraData {} @@ -1094,6 +1098,7 @@ impl From for WalletData { language_preference: mifinity_data.language_preference, }) } + api_models::payments::WalletData::BluecodeRedirect {} => Self::BluecodeRedirect {}, api_models::payments::WalletData::RevolutPay(_) => Self::RevolutPay(RevolutPayData {}), } } @@ -1866,6 +1871,7 @@ impl GetPaymentMethodType for WalletData { Self::GooglePay(_) | Self::GooglePayRedirect(_) | Self::GooglePayThirdPartySdk(_) => { api_enums::PaymentMethodType::GooglePay } + Self::BluecodeRedirect {} => api_enums::PaymentMethodType::Bluecode, Self::MbWayRedirect(_) => api_enums::PaymentMethodType::MbWay, Self::MobilePayRedirect(_) => api_enums::PaymentMethodType::MobilePay, Self::PaypalRedirect(_) | Self::PaypalSdk(_) => api_enums::PaymentMethodType::Paypal, diff --git a/crates/kgraph_utils/src/mca.rs b/crates/kgraph_utils/src/mca.rs index f0f45a2cae..387fec7aeb 100644 --- a/crates/kgraph_utils/src/mca.rs +++ b/crates/kgraph_utils/src/mca.rs @@ -40,6 +40,7 @@ fn get_dir_value_payment_method( Ok(dirval!(PayLaterType = AfterpayClearpay)) } api_enums::PaymentMethodType::GooglePay => Ok(dirval!(WalletType = GooglePay)), + api_enums::PaymentMethodType::Bluecode => Ok(dirval!(WalletType = Bluecode)), api_enums::PaymentMethodType::ApplePay => Ok(dirval!(WalletType = ApplePay)), api_enums::PaymentMethodType::Paypal => Ok(dirval!(WalletType = Paypal)), api_enums::PaymentMethodType::CryptoCurrency => Ok(dirval!(CryptoType = CryptoCurrency)), diff --git a/crates/kgraph_utils/src/transformers.rs b/crates/kgraph_utils/src/transformers.rs index 8261a3645d..fb41de208b 100644 --- a/crates/kgraph_utils/src/transformers.rs +++ b/crates/kgraph_utils/src/transformers.rs @@ -148,6 +148,7 @@ impl IntoDirValue for (api_enums::PaymentMethodType, api_enums::PaymentMethod) { Ok(dirval!(PayLaterType = AfterpayClearpay)) } api_enums::PaymentMethodType::GooglePay => Ok(dirval!(WalletType = GooglePay)), + api_enums::PaymentMethodType::Bluecode => Ok(dirval!(WalletType = Bluecode)), api_enums::PaymentMethodType::ApplePay => Ok(dirval!(WalletType = ApplePay)), api_enums::PaymentMethodType::Paypal => Ok(dirval!(WalletType = Paypal)), api_enums::PaymentMethodType::CryptoCurrency => { diff --git a/crates/payment_methods/src/configs/payment_connector_required_fields.rs b/crates/payment_methods/src/configs/payment_connector_required_fields.rs index 469b27baf8..d53205950d 100644 --- a/crates/payment_methods/src/configs/payment_connector_required_fields.rs +++ b/crates/payment_methods/src/configs/payment_connector_required_fields.rs @@ -3345,6 +3345,26 @@ fn get_bank_transfer_required_fields() -> HashMap matches!( payment_method_type, api_enums::PaymentMethodType::AmazonPay + | api_enums::PaymentMethodType::Bluecode | api_enums::PaymentMethodType::Paysera | api_enums::PaymentMethodType::Skrill | api_enums::PaymentMethodType::ApplePay diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index 653cc01e1f..5deeeadb41 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -2439,6 +2439,7 @@ pub enum PaymentMethodDataType { DanaRedirect, DuitNow, GooglePay, + Bluecode, GooglePayRedirect, GooglePayThirdPartySdk, MbWayRedirect, @@ -2568,6 +2569,7 @@ impl From for PaymentMethodDataType { } domain::payments::WalletData::DanaRedirect {} => Self::DanaRedirect, domain::payments::WalletData::GooglePay(_) => Self::GooglePay, + domain::payments::WalletData::BluecodeRedirect {} => Self::Bluecode, domain::payments::WalletData::GooglePayRedirect(_) => Self::GooglePayRedirect, domain::payments::WalletData::GooglePayThirdPartySdk(_) => { Self::GooglePayThirdPartySdk diff --git a/crates/router/src/core/connector_validation.rs b/crates/router/src/core/connector_validation.rs index 99b873af1d..789bf7c83b 100644 --- a/crates/router/src/core/connector_validation.rs +++ b/crates/router/src/core/connector_validation.rs @@ -129,10 +129,11 @@ impl ConnectorAuthTypeAndMetadataValidation<'_> { bluesnap::transformers::BluesnapAuthType::try_from(self.auth_type)?; Ok(()) } - // api_enums::Connector::Bluecode => { - // bluecode::transformers::BluecodeAuthType::try_from(self.auth_type)?; - // Ok(()) - // } + api_enums::Connector::Bluecode => { + bluecode::transformers::BluecodeAuthType::try_from(self.auth_type)?; + bluecode::transformers::BluecodeMetadataObject::try_from(self.connector_meta_data)?; + Ok(()) + } api_enums::Connector::Braintree => { braintree::transformers::BraintreeAuthType::try_from(self.auth_type)?; braintree::transformers::BraintreeMeta::try_from(self.connector_meta_data)?; diff --git a/crates/router/src/types/api/connector_mapping.rs b/crates/router/src/types/api/connector_mapping.rs index c4a1f187dd..1e89239448 100644 --- a/crates/router/src/types/api/connector_mapping.rs +++ b/crates/router/src/types/api/connector_mapping.rs @@ -145,9 +145,9 @@ impl ConnectorData { enums::Connector::Bluesnap => { Ok(ConnectorEnum::Old(Box::new(connector::Bluesnap::new()))) } - // enums::Connector::Bluecode => { - // Ok(ConnectorEnum::Old(Box::new(connector::Bluecode::new()))) - // } + enums::Connector::Bluecode => { + Ok(ConnectorEnum::Old(Box::new(connector::Bluecode::new()))) + } enums::Connector::Boku => Ok(ConnectorEnum::Old(Box::new(connector::Boku::new()))), enums::Connector::Braintree => { Ok(ConnectorEnum::Old(Box::new(connector::Braintree::new()))) diff --git a/crates/router/src/types/connector_transformers.rs b/crates/router/src/types/connector_transformers.rs index 2563f87191..6939247d48 100644 --- a/crates/router/src/types/connector_transformers.rs +++ b/crates/router/src/types/connector_transformers.rs @@ -22,7 +22,7 @@ impl ForeignTryFrom for common_enums::RoutableConnectors { api_enums::Connector::Billwerk => Self::Billwerk, api_enums::Connector::Bitpay => Self::Bitpay, api_enums::Connector::Bluesnap => Self::Bluesnap, - // api_enums::Connector::Bluecode => Self::Bluecode, + api_enums::Connector::Bluecode => Self::Bluecode, api_enums::Connector::Boku => Self::Boku, api_enums::Connector::Braintree => Self::Braintree, api_enums::Connector::Breadpay => Self::Breadpay, diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index 90f2a42359..0205677472 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -290,7 +290,8 @@ impl ForeignFrom for api_enums::PaymentMethod { | api_enums::PaymentMethodType::KakaoPay | api_enums::PaymentMethodType::Venmo | api_enums::PaymentMethodType::Mifinity - | api_enums::PaymentMethodType::RevolutPay => Self::Wallet, + | api_enums::PaymentMethodType::RevolutPay + | api_enums::PaymentMethodType::Bluecode => Self::Wallet, api_enums::PaymentMethodType::Affirm | api_enums::PaymentMethodType::Alma | api_enums::PaymentMethodType::AfterpayClearpay diff --git a/loadtest/config/development.toml b/loadtest/config/development.toml index a6bf9ead43..bd5c963501 100644 --- a/loadtest/config/development.toml +++ b/loadtest/config/development.toml @@ -603,6 +603,9 @@ eft = { country = "NG, ZA, GH, KE, CI", currency = "NGN, GHS, ZAR, KES, USD" } [pm_filters.santander] pix = { country = "BR", currency = "BRL" } +[pm_filters.bluecode] +bluecode = { country = "AT,BE,BG,HR,CY,CZ,DK,EE,FI,FR,DE,GR,HU,IE,IT,LV,LT,LU,MT,NL,PL,PT,RO,SK,SI,ES,SE,IS,LI,NO", currency = "EUR" } + [pm_filters.airwallex] credit = { country = "AU,HK,SG,NZ,US", currency = "AED,AFN,ALL,AMD,ANG,AOA,ARS,AUD,AWG,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BTN,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CVE,CZK,DJF,DKK,DOP,DZD,EGP,ERN,ETB,EUR,FJD,FKP,GBP,GEL,GHS,GIP,GMD,GNF,GTQ,GYD,HKD,HNL,HRK,HTG,HUF,IDR,ILS,INR,IQD,IRR,ISK,JMD,JOD,JPY,KES,KGS,KHR,KMF,KPW,KRW,KWD,KYD,KZT,LAK,LBP,LKR,LRD,LSL,LYD,MAD,MDL,MGA,MKD,MMK,MNT,MOP,MRU,MUR,MVR,MWK,MXN,MYR,MZN,NAD,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SDG,SEK,SGD,SHP,SLE,SLL,SOS,SRD,SSP,STN,SVC,SYP,SZL,THB,TJS,TMT,TND,TOP,TRY,TTD,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,XOF,XPF,YER,ZAR,ZMW,ZWL" } debit = { country = "AU,HK,SG,NZ,US", currency = "AED,AFN,ALL,AMD,ANG,AOA,ARS,AUD,AWG,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BTN,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CVE,CZK,DJF,DKK,DOP,DZD,EGP,ERN,ETB,EUR,FJD,FKP,GBP,GEL,GHS,GIP,GMD,GNF,GTQ,GYD,HKD,HNL,HRK,HTG,HUF,IDR,ILS,INR,IQD,IRR,ISK,JMD,JOD,JPY,KES,KGS,KHR,KMF,KPW,KRW,KWD,KYD,KZT,LAK,LBP,LKR,LRD,LSL,LYD,MAD,MDL,MGA,MKD,MMK,MNT,MOP,MRU,MUR,MVR,MWK,MXN,MYR,MZN,NAD,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SDG,SEK,SGD,SHP,SLE,SLL,SOS,SRD,SSP,STN,SVC,SYP,SZL,THB,TJS,TMT,TND,TOP,TRY,TTD,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,XOF,XPF,YER,ZAR,ZMW,ZWL" }