cover/Elixir.DaProductApp.Adapters.SandboxPartner.html

1 defmodule DaProductApp.Adapters.SandboxPartner do
2 @moduledoc """
3 Sandbox UPI International partner returning simulated responses.
4
5 Simulates an international partner in Singapore corridor:
6 - Receives SGD after PSP converts INR → SGD
7 - Credits local merchant in SGD
8 - Handles FX-aware QR generation and transactions
9 """
10 @behaviour DaProductApp.Adapters.InternationalPartnerBehaviour
11
12 @impl true
13
:-(
def credit_merchant(params) do
14 # Simulate crediting international merchant in local currency
15 # PSP has already received INR from NPCI and converted to SGD
16 {:ok, %{
17 code: "CS",
18 payload: %{
19 partner_status: "PENDING",
20
:-(
partner_txn_id: "SGP_#{:rand.uniform(999999)}",
21
:-(
local_amount: params.base_amount,
22
:-(
local_currency: params.base_currency,
23
:-(
inr_equivalent: params.inr_amount,
24
:-(
fx_rate_applied: params.fx_rate,
25 merchant_account_credited: true,
26 settlement_status: "PROCESSING"
27 }
28 }}
29 end
30
31 @impl true
32
:-(
def check_transaction_status(params) do
33 # Simulate checking status with international partner
34 {:ok, %{
35 code: "CS",
36 payload: %{
37 partner_status: "SUCCESS",
38 settlement_status: "COMPLETED",
39 merchant_balance_updated: true,
40
:-(
local_amount_settled: params.base_amount,
41
:-(
partner_txn_id: params.partner_txn_id
42 }
43 }}
44 end
45
46 @impl true
47
:-(
def reverse_payment(params) do
48 # Simulate reversing payment from international merchant
49 # Return foreign currency back to PSP for INR reversal to customer
50 {:ok, %{
51 code: "00",
52 payload: %{
53 partner_status: "REVERSED",
54 settlement_status: "REVERSED",
55 reversal_completed: true,
56
:-(
foreign_amount_reversed: params.base_amount,
57
:-(
inr_reversal_required: params.inr_amount
58 }
59 }}
60 end
61
62 @impl true
63 def generate_dynamic_qr_with_fx(params) do
64 # Simulate generating international QR with FX details embedded
65 # This would be called by merchant via partner's system
66
:-(
base_amount = params.base_amount || simulate_fx_conversion(params.inr_amount)
67
68 # Build UPI International QR string with FX parameters
69
:-(
qr_data = build_international_qr(%{
70
:-(
merchant_vpa: params.merchant_vpa,
71
:-(
merchant_name: params.merchant_name,
72 base_amount: base_amount,
73 base_currency: "SGD",
74
:-(
fx_rate: params.fx_rate || "0.0165",
75
:-(
markup: params.markup || "2.50",
76
:-(
inr_amount: params.inr_amount
77 })
78
79 {:ok, %{
80 code: "00",
81 payload: %{
82 qr_code: qr_data,
83
:-(
qr_image_url: "https://api.qrserver.com/v1/create-qr-code/?data=#{URI.encode(qr_data)}",
84 expires_at: DateTime.add(DateTime.utc_now(), 15, :minute),
85 fx_details: %{
86 base_amount: base_amount,
87 base_currency: "SGD",
88
:-(
fx_rate: params.fx_rate || "0.0165",
89
:-(
markup_rate: params.markup || "2.50",
90
:-(
inr_equivalent: params.inr_amount
91 },
92 corridor: "SINGAPORE",
93
:-(
partner_ref: "QR_#{:rand.uniform(999999)}"
94 }
95 }}
96 end
97
98 @impl true
99
:-(
def validate_fx_rates(_params) do
100 # Simulate FX rate validation for the corridor
101 {:ok, %{
102 code: "00",
103 payload: %{
104 rates_valid: true,
105 current_rate: "0.0165",
106 markup_rate: "2.50",
107 rate_expires_at: DateTime.add(DateTime.utc_now(), 5, :minute),
108 corridor: "SINGAPORE"
109 }
110 }}
111 end
112
113 @impl true
114 def get_corridor_info() do
115
:-(
%{
116 currency: "SGD",
117 country: "Singapore",
118 corridor_code: "SGP",
119 supported_features: ["dynamic_qr", "fx_conversion", "real_time_settlement"]
120 }
121 end
122
123 # Private helper functions
124 defp simulate_fx_conversion(inr_amount) when is_binary(inr_amount) do
125
:-(
{inr_float, _} = Float.parse(inr_amount)
126
:-(
simulate_fx_conversion(inr_float)
127 end
128
129 defp simulate_fx_conversion(inr_amount) when is_number(inr_amount) do
130 # Simulate INR to SGD conversion: 1 INR = 0.0165 SGD + 2.5% markup
131
:-(
sgd_rate = 0.0165
132
:-(
markup = 0.025
133
:-(
base_sgd = inr_amount * sgd_rate
134
:-(
sgd_with_markup = base_sgd * (1 + markup)
135
:-(
Float.round(sgd_with_markup, 2)
136 end
137
138 defp build_international_qr(fx_details) do
139 # Build UPI International QR with embedded FX parameters per UPI spec
140
:-(
base_qr = "upi://pay?pa=#{fx_details.merchant_vpa}&pn=#{URI.encode(fx_details.merchant_name)}"
141
142
:-(
fx_params = [
143
:-(
"am=#{fx_details.base_amount}", # Base amount in local currency
144
:-(
"cu=#{fx_details.base_currency}", # Base currency (SGD/USD/AED)
145
:-(
"fx=#{fx_details.fx_rate}", # FX rate
146
:-(
"mkup=#{fx_details.markup}", # Markup percentage
147
:-(
"inr=#{fx_details.inr_amount}", # INR equivalent for customer
148 "mode=17", # UPI International mode
149 "purpose=10" # International merchant payment
150 ]
151
152
:-(
"#{base_qr}&#{Enum.join(fx_params, "&")}"
153 end
154 end
Line Hits Source