1# frozen_string_literal: true
2
3require "bigdecimal/util"
4
5require_relative "low_balance"
6require_relative "transaction"
7
8class SIMOrder
9 def self.for(customer, price:, **kwargs)
10 price = price.to_i / 100.to_d
11 return new(customer, price: price, **kwargs) if customer.balance >= price
12
13 LowBalance::AutoTopUp.for(customer, price).then do |top_up|
14 if top_up.can_top_up?
15 WithTopUp.new(customer, self, price: price, top_up: top_up, **kwargs)
16 else
17 PleaseTopUp.new(price: price, **kwargs)
18 end
19 end
20 end
21
22 def self.label
23 "SIM"
24 end
25
26 def self.fillable_fields
27 [
28 {
29 type: "text-multi",
30 label: "Shipping Address",
31 var: "addr",
32 required: true
33 },
34 {
35 type: "text-single",
36 var: "nickname",
37 label: "Nickname",
38 required: false
39 }
40 ]
41 end
42
43 def initialize(customer, price:, plan:)
44 @customer = customer
45 @price = price
46 @plan = plan
47 @sim_repo = SIMRepo.new(db: DB)
48 end
49
50 def form
51 FormTemplate.render(
52 "order_sim/with_balance",
53 price: @price,
54 plan: @plan,
55 label: self.class.label,
56 fillable_fields: self.class.fillable_fields
57 )
58 end
59
60 def complete(iq)
61 form = iq.form
62 EMPromise.resolve(nil).then {
63 commit(form.field("nickname")&.value.presence || self.class.label)
64 }.then do |sim|
65 Ack.new(
66 @customer,
67 sim,
68 Array(form.field("addr").value).join("\n")
69 ).complete
70 end
71 end
72
73 class Ack
74 def initialize(customer, sim, addr)
75 @customer = customer
76 @sim = sim
77 @addr = addr
78 end
79
80 def complete
81 @customer.stanza_from(Blather::Stanza::Message.new(
82 Blather::JID.new(""), # Doesn't matter, sgx is set to direct target
83 "SIM ORDER: #{@sim.iccid}\n#{@addr}"
84 ))
85 Command.finish(
86 "You will receive an notice from support when your SIM ships."
87 )
88 end
89 end
90
91protected
92
93 # @param [String, nil] nickname the nickname, if any, assigned to
94 # the sim by the customer
95 def commit(nickname)
96 DB.transaction do
97 sim = @sim_repo.available.sync
98 @sim_repo.put_owner(sim, @customer, nickname)
99 keepgo_tx = @sim_repo.refill(sim, amount_mb: 1024).sync
100 raise "SIM activation failed" unless keepgo_tx["ack"] == "success"
101
102 transaction(sim, keepgo_tx).insert_tx
103 sim
104 end
105 end
106
107 def transaction(sim, keepgo_tx)
108 Transaction.new(
109 customer_id: @customer.customer_id,
110 transaction_id: keepgo_tx["transaction_id"],
111 amount: -@price,
112 note: "#{self.class.label} Activation #{sim.iccid}"
113 )
114 end
115
116 class ESIM < SIMOrder
117 def self.label
118 "eSIM"
119 end
120
121 def self.fillable_fields
122 []
123 end
124
125 # @param [Blather::Stanza::Iq] iq the stanza
126 # containing a filled out `order_sim/with_balance`
127 def complete(iq)
128 EMPromise.resolve(nil).then {
129 commit(
130 nickname: iq.form.field("nickname").value.presence || self.class.label
131 )
132 }.then do |sim|
133 ActivationCode.new(sim).complete
134 end
135 end
136 end
137
138 class ActivationCode
139 # @param [Sim] sim the sim which the customer
140 # just ordered
141 def initialize(sim)
142 @sim = sim
143 end
144
145 def complete
146 Command.finish do |reply|
147 oob = OOB.find_or_create(reply.command)
148 oob.url = @sim.lpa_code
149 oob.desc = "LPA Activation Code"
150 reply.command << FormTemplate.render(
151 "order_sim/esim_complete", sim: @sim
152 )
153 end
154 end
155 end
156
157 class WithTopUp
158 def initialize(customer, continue, price:, plan:, top_up:)
159 @customer = customer
160 @price = price
161 @plan = plan
162 @top_up = top_up
163 @continue = continue
164 end
165
166 def form
167 FormTemplate.render(
168 "order_sim/with_top_up",
169 price: @price,
170 plan: @plan,
171 top_up_amount: @top_up.top_up_amount,
172 label: @continue.label,
173 fillable_fields: @continue.fillable_fields
174 )
175 end
176
177 def complete(iq)
178 @top_up.notify!.then do |amount|
179 if amount.positive?
180 @continue.new(@customer, price: @price, plan: @plan).complete(iq)
181 else
182 Command.finish("Could not top up", type: :error)
183 end
184 end
185 end
186 end
187
188 class PleaseTopUp
189 def initialize(price:, plan:)
190 @price = price
191 @plan = plan
192 end
193
194 def form
195 FormTemplate.render(
196 "order_sim/please_top_up",
197 price: @price,
198 plan: @plan
199 )
200 end
201
202 def complete(_)
203 Command.finish
204 end
205 end
206end