From 6f8c199852666e00b771ff82126606edaedd502b Mon Sep 17 00:00:00 2001 From: Hector Carrillo Date: Tue, 3 Feb 2026 19:14:47 -0300 Subject: [PATCH 1/3] update payments --- lib/solidgate/client.rb | 68 +++++++++++++++++-------- lib/solidgate/payment.rb | 11 ++-- spec/solidgate/client_spec.rb | 93 ++++++++++++++++++++++++++++++++++ spec/solidgate/payment_spec.rb | 4 +- 4 files changed, 149 insertions(+), 27 deletions(-) diff --git a/lib/solidgate/client.rb b/lib/solidgate/client.rb index 468c33f..cb53338 100644 --- a/lib/solidgate/client.rb +++ b/lib/solidgate/client.rb @@ -60,7 +60,7 @@ def initialize(options = Solidgate.configuration) # @raise [AuthenticationError] if API credentials are invalid # def create_payment(params) - post("/v1/charge", params) + post("/v1/charge", body: params) end # Retrieves payment details and current status. @@ -88,7 +88,7 @@ def get_payment(payment_id) # @raise [InvalidRequestError] if payment cannot be captured (wrong status, already captured, etc.) # def capture_payment(payment_id, params = {}) - post("/v1/charge/#{payment_id}/capture", params) + post("/v1/charge/#{payment_id}/capture", body: params) end # Voids a payment that has not yet been settled. @@ -113,7 +113,7 @@ def void_payment(payment_id) # @raise [InvalidRequestError] if payment cannot be refunded # def refund_payment(payment_id, params = {}) - post("/v1/charge/#{payment_id}/refund", params) + post("/v1/charge/#{payment_id}/refund", body: params) end # Settles a payment for final processing. @@ -138,7 +138,7 @@ def settle_payment(params = {}) # @raise [InvalidRequestError] if subscription parameters are invalid # def create_subscription(params) - post("/v1/subscription", params) + post("/v1/subscription", body: params) end # Retrieves the current status and details of a subscription. @@ -152,7 +152,7 @@ def create_subscription(params) # @raise [InvalidRequestError] if subscription_id is not found # def subscription_status(subscription_id) - post("/api/v1/subscription/status", { subscription_id: subscription_id }) + post("/api/v1/subscription/status", body: { subscription_id: subscription_id }) end # Switches a subscription to a different product/plan. @@ -171,7 +171,7 @@ def subscription_status(subscription_id) # ) # def switch_subscription_product(params) - post("/api/v1/subscription/switch-subscription-product", params) + post("/api/v1/subscription/switch-subscription-product", body: params) end # Updates an existing pause schedule for a subscription. @@ -184,7 +184,7 @@ def switch_subscription_product(params) # @raise [InvalidRequestError] if subscription has no pause schedule or params are invalid # def update_subscription_pause(subscription_id, params) - patch("/api/v1/subscriptions/#{subscription_id}/pause-schedule", params) + patch("/api/v1/subscriptions/#{subscription_id}/pause-schedule", body: params) end # Creates a pause schedule for a subscription. @@ -198,7 +198,7 @@ def update_subscription_pause(subscription_id, params) # @raise [InvalidRequestError] if subscription is invalid or already has a pause schedule # def create_subscription_pause(subscription_id, params) - post("/api/v1/subscriptions/#{subscription_id}/pause-schedule", params) + post("/api/v1/subscriptions/#{subscription_id}/pause-schedule", body: params) end # Deletes/cancels a pending pause schedule for a subscription. @@ -221,7 +221,7 @@ def delete_subscription_pause(subscription_id) # @raise [InvalidRequestError] if subscription is invalid or already cancelled # def cancel_subscription(params) - post("/api/v1/subscription/cancel", params) + post("/api/v1/subscription/cancel", body: params) end # Creates a new product in the Solidgate catalog. @@ -234,7 +234,7 @@ def cancel_subscription(params) # @raise [InvalidRequestError] if product parameters are invalid # def create_product(params) - post("/api/v1/products", params) + post("/api/v1/products", body: params) end # Creates a new price for an existing product. @@ -248,7 +248,7 @@ def create_product(params) # @raise [InvalidRequestError] if product_id is invalid or price params are invalid # def create_price(product_id, params) - post("/api/v1/products/#{product_id}/prices", params) + post("/api/v1/products/#{product_id}/prices", body: params) end # Retrieves all products from the Solidgate catalog. @@ -322,7 +322,22 @@ def generate_signature(json_string, public_key: config.public_key, private_key: # client.restore_subscription(subscription_id: 'sub_12345') # def restore_subscription(params) - post("/api/v1/subscription/restore", params) + post("/api/v1/subscription/restore", body: params) + end + + # Creates a refund for a transaction. + # + # @param params [Hash] refund parameters: + # - :order_id [String] the order identifier to refund + # - :amount [Integer] refund amount in minor units (for partial refunds) + # @return [Hash] refund response including refund status and details + # @raise [InvalidRequestError] if refund parameters are invalid + # + # @example Create a refund + # client.refund(order_id: 'ord_12345', amount: 1000) + # + def refund(params) + post("/api/v1/refund", body: params, base_url: "https://pay.solidgate.com") end private @@ -347,8 +362,17 @@ def build_config(options) # @return [Faraday::Connection] configured HTTP connection # def connection - @connection ||= Faraday.new( - url: config.api_url, + @connection ||= connection_for(config.api_url) + end + + # Creates a Faraday HTTP connection for the specified base URL. + # + # @param base_url [String] the base URL for the connection + # @return [Faraday::Connection] configured HTTP connection + # + def connection_for(base_url) + Faraday.new( + url: base_url, headers: default_headers ) do |conn| conn.request :multipart @@ -386,10 +410,11 @@ def get(path) # # @param path [String] API endpoint path # @param body [Hash] request body parameters + # @param base_url [String, nil] optional base URL to override the default API URL # @return [Hash] parsed response body # - def post(path, body = {}) - request(:post, path, body) + def post(path, body: {}, base_url: nil) + request(:post, path, body: body, base_url: base_url) end # Performs a PATCH request to the specified path. @@ -398,8 +423,8 @@ def post(path, body = {}) # @param body [Hash] request body parameters # @return [Hash] parsed response body # - def patch(path, body = {}) - request(:patch, path, body) + def patch(path, body: {}) + request(:patch, path, body: body) end # Performs a DELETE request to the specified path. @@ -416,16 +441,19 @@ def delete(path) # @param method [Symbol] HTTP method (:get, :post, :patch, :delete) # @param path [String] API endpoint path # @param body [Hash, nil] optional request body + # @param base_url [String, nil] optional base URL to override the default API URL # @return [Hash] parsed response body # @raise [TimeoutError] if request times out # @raise [ConnectionError] if connection fails # @raise [Error] for other unexpected errors # - def request(method, path, body = nil) + def request(method, path, body: nil, base_url: nil) body_json = body ? JSON.generate(body) : '' signature = generate_signature(body_json) - response = connection.send(method) do |req| + conn = base_url ? connection_for(base_url) : connection + + response = conn.send(method) do |req| req.url path req.headers["Merchant"] = config.public_key req.headers["Signature"] = signature diff --git a/lib/solidgate/payment.rb b/lib/solidgate/payment.rb index 05e935e..3f10c17 100644 --- a/lib/solidgate/payment.rb +++ b/lib/solidgate/payment.rb @@ -64,14 +64,15 @@ def void(payment_id) # @param amount [Integer] amount to refund in cents (optional, defaults to full amount) # @param reason [String] refund reason (optional) # @return [Hash] refund response - def refund(payment_id, amount: nil, reason: nil) - raise ArgumentError, "payment_id is required" if payment_id.nil? || payment_id.empty? + def refund(order_id, amount: nil, reason: nil) + raise ArgumentError, "order_id is required" if order_id.nil? || order_id.empty? params = {} - params[:amount] = amount if amount - params[:reason] = reason if reason + params[:amount] = amount if amount + params[:reason] = reason if reason + params[:order_id] = order_id - client.refund_payment(payment_id, params) + client.refund(params) end private diff --git a/spec/solidgate/client_spec.rb b/spec/solidgate/client_spec.rb index 058b988..2bad10b 100644 --- a/spec/solidgate/client_spec.rb +++ b/spec/solidgate/client_spec.rb @@ -363,6 +363,99 @@ end end + describe "#refund" do + let(:refund_params) do + { + order_id: "order_123", + amount: 1000 + } + end + + before do + stub_request(:post, /pay\.solidgate\.com/).to_return( + status: 200, + body: success_response.to_json, + headers: { "Content-Type" => "application/json" } + ) + end + + it "sends POST request to https://pay.solidgate.com/api/v1/refund" do + client.refund(refund_params) + + expect(WebMock).to have_requested(:post, "https://pay.solidgate.com/api/v1/refund") + .with(body: refund_params.to_json) + end + + it "uses the pay subdomain instead of subscriptions subdomain" do + client.refund(refund_params) + + expect(WebMock).not_to have_requested(:post, /subscriptions\.solidgate\.com/) + expect(WebMock).to have_requested(:post, /pay\.solidgate\.com/) + end + + it "includes Merchant header with public key" do + client.refund(refund_params) + + expect(WebMock).to have_requested(:post, "https://pay.solidgate.com/api/v1/refund") + .with(headers: { "Merchant" => public_key }) + end + + it "includes Signature header" do + client.refund(refund_params) + + expect(WebMock).to have_requested(:post, "https://pay.solidgate.com/api/v1/refund") + .with { |req| !req.headers["Signature"].nil? && !req.headers["Signature"].empty? } + end + + it "returns refund response" do + result = client.refund(refund_params) + expect(result).to eq(success_response) + end + end + + # ==================== Base URL Override ==================== + + describe "base_url parameter" do + let(:custom_base_url) { "https://custom.solidgate.com" } + let(:params) { { order_id: "order_123" } } + + before do + stub_request(:post, /custom\.solidgate\.com/).to_return( + status: 200, + body: success_response.to_json, + headers: { "Content-Type" => "application/json" } + ) + end + + it "uses custom base_url when provided via post method" do + client.send(:post, "/api/v1/test", body: params, base_url: custom_base_url) + + expect(WebMock).to have_requested(:post, "https://custom.solidgate.com/api/v1/test") + .with(body: params.to_json) + end + + it "uses default api_url when base_url is nil" do + client.send(:post, "/api/v1/test", body: params, base_url: nil) + + expect(WebMock).to have_requested(:post, "https://subscriptions.solidgate.com/api/v1/test") + .with(body: params.to_json) + end + + it "includes proper headers when using custom base_url" do + client.send(:post, "/api/v1/test", body: params, base_url: custom_base_url) + + expect(WebMock).to have_requested(:post, "https://custom.solidgate.com/api/v1/test") + .with(headers: { "Merchant" => public_key }) + end + + it "includes signature when using custom base_url" do + client.send(:post, "/api/v1/test", body: params, base_url: custom_base_url) + + expect(WebMock).to have_requested(:post, "https://custom.solidgate.com/api/v1/test") + .with { |req| !req.headers["Signature"].nil? && !req.headers["Signature"].empty? } + end + end + # ==================== Product Methods ==================== describe "#create_product" do diff --git a/spec/solidgate/payment_spec.rb b/spec/solidgate/payment_spec.rb index 0a46c26..32a8939 100644 --- a/spec/solidgate/payment_spec.rb +++ b/spec/solidgate/payment_spec.rb @@ -109,12 +109,12 @@ describe "#refund" do it "refunds a payment" do - expect(client).to receive(:refund_payment).with("payment_123", {}) + expect(client).to receive(:refund).with(order_id: "payment_123") payment.refund("payment_123") end it "refunds with specific amount and reason" do - expect(client).to receive(:refund_payment).with("payment_123", { amount: 500, reason: "Customer request" }) + expect(client).to receive(:refund).with(order_id: "payment_123", amount: 500, reason: "Customer request") payment.refund("payment_123", amount: 500, reason: "Customer request") end end From d9cd9cc9e1547c32d7c8e5ea273b45d3a9f578f6 Mon Sep 17 00:00:00 2001 From: Hector Carrillo Date: Tue, 3 Feb 2026 19:17:50 -0300 Subject: [PATCH 2/3] fix ci --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 717c809..52de419 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: '3.2' + ruby-version: '2.7' bundler-cache: true - name: Install dependencies From b5d4b1d9d522494b0f88544d6207d41e1edb8a58 Mon Sep 17 00:00:00 2001 From: Hector Carrillo Date: Tue, 3 Feb 2026 19:21:03 -0300 Subject: [PATCH 3/3] update version file --- Gemfile.lock | 2 +- lib/solidgate/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 543e5c0..a42c34e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - solidgate-ruby-sdk (0.1.9) + solidgate-ruby-sdk (0.1.10) faraday faraday-multipart diff --git a/lib/solidgate/version.rb b/lib/solidgate/version.rb index f826a80..c65ac4e 100644 --- a/lib/solidgate/version.rb +++ b/lib/solidgate/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Solidgate - VERSION = "0.1.9" + VERSION = "0.1.10" end