From c11b93a4dcd22ffbc1c61223cd75023823e30f6f Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Mon, 19 Jan 2026 14:28:00 +1100 Subject: [PATCH] Demo Taler flow without validating payment yet --- .../payment_gateways/taler_controller.rb | 16 +++++++ app/models/spree/payment_method/taler.rb | 43 ++++++++++++++++--- config/locales/en.yml | 2 + config/routes.rb | 2 + .../models/spree/payment_method/taler_spec.rb | 9 +++- spec/requests/payment_gateways/taler_spec.rb | 27 ++++++++++++ 6 files changed, 92 insertions(+), 7 deletions(-) create mode 100644 app/controllers/payment_gateways/taler_controller.rb create mode 100644 spec/requests/payment_gateways/taler_spec.rb diff --git a/app/controllers/payment_gateways/taler_controller.rb b/app/controllers/payment_gateways/taler_controller.rb new file mode 100644 index 0000000000..aff2799242 --- /dev/null +++ b/app/controllers/payment_gateways/taler_controller.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module PaymentGateways + class TalerController < BaseController + include OrderCompletion + + # The Taler merchant backend has taken the payment. + # Now we just need to confirm that and update our local database + # before finalising the order. + def confirm + payment = Spree::Payment.find(params[:payment_id]) + @order = payment.order + process_payment_completion! + end + end +end diff --git a/app/models/spree/payment_method/taler.rb b/app/models/spree/payment_method/taler.rb index fed3239d7f..41d0881a43 100644 --- a/app/models/spree/payment_method/taler.rb +++ b/app/models/spree/payment_method/taler.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "taler" + module Spree class PaymentMethod # GNU Taler is a distributed, open source payment system. @@ -32,15 +34,46 @@ module Spree # https://backend.demo.taler.net/instances/blog/orders/2026..?token=S8Y..&session_id=b0b.. def external_payment_url(options) order = options.fetch(:order) - total_amount = order&.total || 5 - taler_amount = "KUDOS:#{total_amount}" - new_order = client.create_order(taler_amount, "OFN Order", "https://ofn.example.net") - order = client.fetch_order(new_order["order_id"]) - order["order_status_url"] + payment = load_payment(order) + + payment.source ||= self + payment.response_code ||= create_taler_order(payment) + payment.redirect_auth_url ||= fetch_order_url(payment) + payment.save! if payment.changed? + + payment.redirect_auth_url + end + + def purchase(_money, _creditcard, _gateway_options) + # TODO: implement + ActiveMerchant::Billing::Response.new(true, "test") end private + def load_payment(order) + order.payments.checkout.where(payment_method: self).last + end + + def create_taler_order(payment) + # We are ignoring currency for now so that we can test with the + # current demo backend only working with the KUDOS currency. + taler_amount = "KUDOS:#{payment.amount}" + urls = Rails.application.routes.url_helpers + new_order = client.create_order( + taler_amount, + I18n.t("payment_method_taler.order_summary"), + urls.payment_gateways_confirm_taler_url(payment_id: payment.id), + ) + + new_order["order_id"] + end + + def fetch_order_url(payment) + order = client.fetch_order(payment.response_code) + order["order_status_url"] + end + def client @client ||= ::Taler::Client.new(preferred_backend_url, preferred_api_key) end diff --git a/config/locales/en.yml b/config/locales/en.yml index 254b8b811b..1d081df210 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -3502,6 +3502,8 @@ en: payment_processing_failed: "Payment could not be processed, please check the details you entered" payment_method_not_supported: "That payment method is unsupported. Please choose another one." payment_updated: "Payment Updated" + payment_method_taler: + order_summary: "Open Food Network order" cannot_perform_operation: "Could not update the payment" action_required: "Action required" tag_rules: "Tag Rules" diff --git a/config/routes.rb b/config/routes.rb index 0946d21ab7..abc399163b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -82,6 +82,8 @@ Openfoodnetwork::Application.routes.draw do get "/stripe/confirm", to: "stripe#confirm", as: :confirm_stripe get "/stripe/authorize/:order_number", to: "stripe#authorize", as: :authorize_stripe + + get "/taler/:payment_id", to: "taler#confirm", as: :confirm_taler end get '/checkout', to: 'checkout#edit' diff --git a/spec/models/spree/payment_method/taler_spec.rb b/spec/models/spree/payment_method/taler_spec.rb index c30d7064f4..25c619b5fc 100644 --- a/spec/models/spree/payment_method/taler_spec.rb +++ b/spec/models/spree/payment_method/taler_spec.rb @@ -11,9 +11,14 @@ RSpec.describe Spree::PaymentMethod::Taler do } describe "external_payment_url", vcr: true do - it "retrieves a URL to pay at" do - url = subject.external_payment_url(order: nil) + it "retrieves a URL to pay at and stores it on the payment record" do + order = create(:order_ready_for_confirmation, payment_method: taler) + url = subject.external_payment_url(order:) expect(url).to match %r{\Ahttps://backend.demo.taler.net/instances/sandb} + + payment = order.payments.last.reload + expect(payment.response_code).to match "2026.022-0284X4GE8WKMJ" + expect(payment.redirect_auth_url).to eq url end end end diff --git a/spec/requests/payment_gateways/taler_spec.rb b/spec/requests/payment_gateways/taler_spec.rb new file mode 100644 index 0000000000..ed9969e583 --- /dev/null +++ b/spec/requests/payment_gateways/taler_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe "/payment_gateways/taler/:id" do + it "completes the order" do + shop = create(:distributor_enterprise) + taler = Spree::PaymentMethod::Taler.create!( + name: "Taler", + environment: "test", + distributors: [shop], + ) + order = create(:order_ready_for_confirmation, payment_method: taler) + payment = Spree::Payment.last + payment.update!( + payment_method: taler, + response_code: "taler-order-id:12345", + redirect_auth_url: "https://merchant.backend.where-we-paid.com", + ) + + get payment_gateways_confirm_taler_path(payment_id: payment.id) + expect(response).to redirect_to(order_path(order, order_token: order.token)) + + payment.reload + expect(payment.state).to eq "completed" + end +end