diff --git a/app/controllers/letters_controller.rb b/app/controllers/letters_controller.rb index 5e80efe..7b40f65 100644 --- a/app/controllers/letters_controller.rb +++ b/app/controllers/letters_controller.rb @@ -183,8 +183,6 @@ class LettersController < ApplicationController return end - indicium = USPS::Indicium.new(letter: @letter, payment_account: usps_payment_account) - hcb_payment_account = current_user.hcb_payment_accounts.find_by(id: params[:hcb_payment_account_id]) if hcb_payment_account.blank? @@ -192,12 +190,31 @@ class LettersController < ApplicationController return end - service = HCB::IndiciumPurchaseService.new(indicium: indicium, hcb_payment_account: hcb_payment_account) - if service.call - redirect_to @letter, notice: "Indicia purchased successfully (charged to #{hcb_payment_account.organization_name})." - else - redirect_to @letter, alert: service.errors.join(", ") + indicium = USPS::Indicium.new(letter: @letter, payment_account: usps_payment_account) + cost_cents = (@letter.postage * 100).ceil + + ActiveRecord::Base.transaction do + transfer_service = HCB::TransferService.new( + hcb_payment_account: hcb_payment_account, + amount_cents: cost_cents, + memo: "Theseus postage: #{@letter.public_id}", + ) + transfer = transfer_service.call + + unless transfer + redirect_to @letter, alert: transfer_service.errors.join(", ") + return + end + + indicium.hcb_payment_account = hcb_payment_account + indicium.hcb_transfer_id = transfer.id + indicium.save! + indicium.buy! end + + redirect_to @letter, notice: "Indicia purchased successfully (charged to #{hcb_payment_account.organization_name})." + rescue => e + redirect_to @letter, alert: "Purchase failed: #{e.message}" end private diff --git a/app/models/letter/batch.rb b/app/models/letter/batch.rb index 4abbce8..e7e6151 100644 --- a/app/models/letter/batch.rb +++ b/app/models/letter/batch.rb @@ -154,18 +154,46 @@ class Letter::Batch < Batch generate_labels(options) end - # Purchase indicia for all letters in the batch using a single payment token - # Requires hcb_payment_account to create a disbursement for the whole batch def purchase_batch_indicia(usps_payment_account, hcb_payment_account:) raise ArgumentError, "HCB payment account is required to purchase indicia" if hcb_payment_account.nil? + raise ArgumentError, "USPS payment account is required to purchase indicia" if usps_payment_account.nil? - service = HCB::BatchPurchaseService.new( - batch: self, - hcb_payment_account: hcb_payment_account, - usps_payment_account: usps_payment_account, - ) - unless service.call - raise StandardError, service.errors.join(", ") + letters_needing_indicia = letters.select do |letter| + letter.postage_type == "indicia" && letter.usps_indicium.nil? + end + + return if letters_needing_indicia.empty? + + total_cost_cents = letters_needing_indicia.sum do |letter| + (letter.postage * 100).ceil + end + + ActiveRecord::Base.transaction do + transfer_service = HCB::TransferService.new( + hcb_payment_account: hcb_payment_account, + amount_cents: total_cost_cents, + memo: "Theseus batch postage: #{letters.count} letters", + ) + transfer = transfer_service.call + unless transfer + raise StandardError, transfer_service.errors.join(", ") + end + + update!( + hcb_payment_account: hcb_payment_account, + hcb_transfer_id: transfer.id, + ) + + payment_token = usps_payment_account.create_payment_token + letters_needing_indicia.each do |letter| + indicium = USPS::Indicium.create!( + letter: letter, + payment_account: usps_payment_account, + hcb_payment_account: hcb_payment_account, + mailing_date: letter_mailing_date, + ) + indicium.buy!(payment_token) + end end end diff --git a/app/models/letter/instant_queue.rb b/app/models/letter/instant_queue.rb index 1c3eab3..462e55e 100644 --- a/app/models/letter/instant_queue.rb +++ b/app/models/letter/instant_queue.rb @@ -90,18 +90,30 @@ class Letter::InstantQueue < Letter::Queue usps_payment_account = USPS::PaymentAccount.find(usps_payment_account_id) Rails.logger.info("Found USPS payment account #{usps_payment_account.id}") - indicium = USPS::Indicium.create!( + indicium = USPS::Indicium.new( letter: letter, payment_account: usps_payment_account, mailing_date: letter.mailing_date, ) - Rails.logger.info("Created indicium #{indicium.id} for letter #{letter.id}") + Rails.logger.info("Created indicium for letter #{letter.id}") + + cost_cents = (letter.postage * 100).ceil Rails.logger.info("Using HCB payment account #{hcb_payment_account.id} for letter #{letter.id}") - service = HCB::IndiciumPurchaseService.new(indicium: indicium, hcb_payment_account: hcb_payment_account) - unless service.call - raise "HCB payment failed: #{service.errors.join(', ')}" + transfer_service = HCB::TransferService.new( + hcb_payment_account: hcb_payment_account, + amount_cents: cost_cents, + memo: "Theseus postage: #{letter.public_id}", + ) + transfer = transfer_service.call + unless transfer + raise "HCB payment failed: #{transfer_service.errors.join(', ')}" end + + indicium.hcb_payment_account = hcb_payment_account + indicium.hcb_transfer_id = transfer.id + indicium.save! + indicium.buy! Rails.logger.info("Successfully bought indicium for letter #{letter.id}") letter.reload diff --git a/app/services/hcb/batch_purchase_service.rb b/app/services/hcb/batch_purchase_service.rb deleted file mode 100644 index b45ff88..0000000 --- a/app/services/hcb/batch_purchase_service.rb +++ /dev/null @@ -1,79 +0,0 @@ -class HCB::BatchPurchaseService - class DisbursementError < StandardError; end - - attr_reader :batch, :hcb_payment_account, :usps_payment_account, :errors - - def initialize(batch:, hcb_payment_account:, usps_payment_account:) - @batch = batch - @hcb_payment_account = hcb_payment_account - @usps_payment_account = usps_payment_account - @errors = [] - end - - def call - return failure("No HCB payment account provided") unless hcb_payment_account - return failure("No USPS payment account provided") unless usps_payment_account - - letters_needing_indicia = batch.letters.select do |letter| - letter.postage_type == "indicia" && letter.usps_indicium.nil? - end - - return true if letters_needing_indicia.empty? - - total_cost_cents = estimate_total_cost_cents(letters_needing_indicia) - - ActiveRecord::Base.transaction do - transfer = create_disbursement!(total_cost_cents) - batch.update!( - hcb_payment_account: hcb_payment_account, - hcb_transfer_id: transfer.id, - ) - - purchase_indicia_for_letters!(letters_needing_indicia) - end - - true - rescue HCBV4::APIError => e - failure("HCB disbursement failed: #{e.message}") - rescue => e - failure("Batch purchase failed: #{e.message}") - end - - private - - def create_disbursement!(amount_cents) - hcb_payment_account.create_disbursement!( - amount_cents: amount_cents, - memo: "Theseus batch postage: #{batch.letters.count} letters", - ) - end - - def purchase_indicia_for_letters!(letters) - payment_token = usps_payment_account.create_payment_token - - letters.each do |letter| - indicium = USPS::Indicium.create!( - letter: letter, - payment_account: usps_payment_account, - hcb_payment_account: hcb_payment_account, - mailing_date: batch.letter_mailing_date, - ) - indicium.buy!(payment_token) - end - end - - def estimate_total_cost_cents(letters) - letters.sum do |letter| - if letter.processing_category == "flat" - 150 - else - 73 - end - end - end - - def failure(message) - @errors << message - false - end -end diff --git a/app/services/hcb/indicium_purchase_service.rb b/app/services/hcb/indicium_purchase_service.rb deleted file mode 100644 index 0902f13..0000000 --- a/app/services/hcb/indicium_purchase_service.rb +++ /dev/null @@ -1,61 +0,0 @@ -class HCB::IndiciumPurchaseService - class DisbursementError < StandardError; end - - attr_reader :indicium, :hcb_payment_account, :errors - - def initialize(indicium:, hcb_payment_account:) - @indicium = indicium - @hcb_payment_account = hcb_payment_account - @errors = [] - end - - def call - return failure("No HCB payment account provided") unless hcb_payment_account - return failure("No letter attached to indicium") unless indicium.letter - - estimated_cost_cents = estimate_cost_cents - - ActiveRecord::Base.transaction do - transfer = create_disbursement!(estimated_cost_cents) - indicium.hcb_payment_account = hcb_payment_account - indicium.hcb_transfer_id = transfer.id - indicium.save! - - indicium.buy! - end - - true - rescue HCBV4::APIError => e - failure("HCB disbursement failed: #{e.message}") - rescue => e - failure("Purchase failed: #{e.message}") - end - - private - - def create_disbursement!(amount_cents) - hcb_payment_account.create_disbursement!( - amount_cents: amount_cents, - memo: disbursement_memo, - ) - end - - def estimate_cost_cents - letter = indicium.letter - base_cents = if letter.processing_category == "flat" - 150 - else - 73 - end - (base_cents * 1.0).ceil - end - - def disbursement_memo - "Theseus postage: #{indicium.letter.public_id}" - end - - def failure(message) - @errors << message - false - end -end