This commit is contained in:
24c02 2025-12-18 15:18:48 -05:00
parent b375b265b9
commit c89d3fc95b
5 changed files with 78 additions and 161 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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