This commit is contained in:
24c02 2025-12-18 15:43:27 -05:00
parent c89d3fc95b
commit 8ba8b36e26
5 changed files with 113 additions and 39 deletions

View file

@ -190,31 +190,43 @@ class LettersController < ApplicationController
return
end
indicium = USPS::Indicium.new(letter: @letter, payment_account: usps_payment_account)
indicium = USPS::Indicium.create!(
letter: @letter,
payment_account: usps_payment_account,
hcb_payment_account: hcb_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
transfer_service = HCB::TransferService.new(
hcb_payment_account: hcb_payment_account,
amount_cents: cost_cents,
name: "Postage for #{@letter.public_id} #{indicium.public_id} #{letter_path(@letter)}",
memo: "[theseus] postage for a #{@letter.processing_category}",
)
transfer = transfer_service.call
unless transfer
redirect_to @letter, alert: transfer_service.errors.join(", ")
return
end
unless transfer
indicium.destroy!
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.update!(hcb_transfer_id: transfer.id)
begin
indicium.buy!
rescue => e
HCB::PaymentAccount.refund_to_organization!(
organization_id: hcb_payment_account.organization_id,
amount_cents: cost_cents,
name: "Refund for #{@letter.public_id} #{indicium.public_id} #{letter_path(@letter)}",
memo: "[theseus] postage refund for a #{@letter.processing_category}",
)
redirect_to @letter, alert: "Purchase failed: #{e.message}"
return
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

@ -27,6 +27,32 @@ class HCB::PaymentAccount < ApplicationRecord
validates :organization_id, presence: true, uniqueness: { scope: :user_id }
validates :organization_name, presence: true
def self.theseus_client
HCBV4::Client.from_credentials(
client_id: ENV.fetch("HCB_CLIENT_ID"),
client_secret: ENV.fetch("HCB_CLIENT_SECRET"),
access_token: ENV.fetch("HCB_SERVICE_ACCESS_TOKEN"),
refresh_token: ENV.fetch("HCB_SERVICE_REFRESH_TOKEN"),
)
end
def self.refund_to_organization!(organization_id:, amount_cents:, name:, memo: nil)
result = theseus_client.create_disbursement(
event_id: ENV.fetch("HCB_RECIPIENT_ORG_ID"),
to_organization_id: organization_id,
amount_cents: amount_cents,
name: name,
)
if memo && result.transaction_id
theseus_client.update_transaction(
result.transaction_id,
event_id: ENV.fetch("HCB_RECIPIENT_ORG_ID"),
memo: memo
)
end
result
end
def client
oauth_connection.client
end
@ -35,13 +61,16 @@ class HCB::PaymentAccount < ApplicationRecord
client.organization!(organization_id)
end
def create_disbursement!(amount_cents:, memo:)
def create_disbursement!(amount_cents:, name:, memo: nil)
result = client.create_disbursement(
event_id: organization_id,
to_organization_id: ENV.fetch("HCB_RECIPIENT_ORG_ID"),
amount_cents: amount_cents,
name: memo,
name: name,
)
if memo && result.transaction_id
client.update_transaction(result.transaction_id, event_id: organization_id, memo: memo)
end
oauth_connection.persist_refreshed_token!
result
end

View file

@ -168,23 +168,42 @@ class Letter::Batch < Batch
(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
letter_count = letters_needing_indicia.count { |l| l.processing_category == "letter" }
flat_count = letters_needing_indicia.count { |l| l.processing_category == "flat" }
batch_description = [
("#{letter_count} #{"letter".pluralize(letter_count)}" if letter_count > 0),
("#{flat_count} #{"flat".pluralize(flat_count)}" if flat_count > 0),
].compact.join(" and ")
transfer_service = HCB::TransferService.new(
hcb_payment_account: hcb_payment_account,
amount_cents: total_cost_cents,
name: "Batch postage for #{public_id} (#{letters_needing_indicia.count} letters) #{Rails.application.routes.url_helpers.letter_batch_path(self)}",
memo: "[theseus] postage for a batch of #{batch_description}",
)
transfer = transfer_service.call
unless transfer
raise StandardError, transfer_service.errors.join(", ")
end
begin
payment_token = usps_payment_account.create_payment_token
rescue => e
HCB::PaymentAccount.refund_to_organization!(
organization_id: hcb_payment_account.organization_id,
amount_cents: total_cost_cents,
name: "Refund for batch #{public_id} #{Rails.application.routes.url_helpers.letter_batch_path(self)}",
memo: "[theseus] postage refund for a batch of #{batch_description}",
)
raise e
end
ActiveRecord::Base.transaction do
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,

View file

@ -90,12 +90,13 @@ 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.new(
indicium = USPS::Indicium.create!(
letter: letter,
payment_account: usps_payment_account,
hcb_payment_account: hcb_payment_account,
mailing_date: letter.mailing_date,
)
Rails.logger.info("Created indicium for letter #{letter.id}")
Rails.logger.info("Created indicium #{indicium.public_id} for letter #{letter.id}")
cost_cents = (letter.postage * 100).ceil
@ -103,17 +104,28 @@ class Letter::InstantQueue < Letter::Queue
transfer_service = HCB::TransferService.new(
hcb_payment_account: hcb_payment_account,
amount_cents: cost_cents,
memo: "Theseus postage: #{letter.public_id}",
name: "Postage for #{letter.public_id} #{indicium.public_id} #{Rails.application.routes.url_helpers.letter_path(letter)}",
memo: "[theseus] postage for a #{letter.processing_category}",
)
transfer = transfer_service.call
unless transfer
indicium.destroy!
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!
indicium.update!(hcb_transfer_id: transfer.id)
begin
indicium.buy!
rescue => e
HCB::PaymentAccount.refund_to_organization!(
organization_id: hcb_payment_account.organization_id,
amount_cents: cost_cents,
name: "Refund for #{letter.public_id} #{indicium.public_id} #{Rails.application.routes.url_helpers.letter_path(letter)}",
memo: "[theseus] postage refund for a #{letter.processing_category}",
)
raise e
end
Rails.logger.info("Successfully bought indicium for letter #{letter.id}")
letter.reload

View file

@ -1,9 +1,10 @@
class HCB::TransferService
attr_reader :hcb_payment_account, :amount_cents, :memo, :errors
attr_reader :hcb_payment_account, :amount_cents, :name, :memo, :errors
def initialize(hcb_payment_account:, amount_cents:, memo:)
def initialize(hcb_payment_account:, amount_cents:, name:, memo: nil)
@hcb_payment_account = hcb_payment_account
@amount_cents = amount_cents
@name = name
@memo = memo
@errors = []
end
@ -14,6 +15,7 @@ class HCB::TransferService
transfer = hcb_payment_account.create_disbursement!(
amount_cents: amount_cents,
name: name,
memo: memo,
)