Skip to content

Commit 88b0745

Browse files
feat(client): add webhook support
1 parent df3f09b commit 88b0745

7 files changed

Lines changed: 31 additions & 1 deletion

File tree

Gemfile.lock

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ PATH
2121
lithic (0.7.0)
2222
cgi
2323
connection_pool
24+
standardwebhooks
2425

2526
GEM
2627
remote: https://rubygems.org/
@@ -150,6 +151,7 @@ GEM
150151
rexml (>= 3.2.6)
151152
sorbet-static-and-runtime (>= 0.5.10187)
152153
thor (>= 0.19.2)
154+
standardwebhooks (1.0.1)
153155
steep (1.10.0)
154156
activesupport (>= 5.1)
155157
concurrent-ruby (>= 1.1.10)

lib/lithic.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
# Gems.
3232
require "connection_pool"
33+
require "standardwebhooks"
3334

3435
# Package files.
3536
require_relative "lithic/version"

lib/lithic/client.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ class Client < Lithic::Internal::Transport::BaseClient
2323
# @return [String]
2424
attr_reader :api_key
2525

26+
# @return [String, nil]
27+
attr_reader :webhook_secret
28+
2629
# @return [Lithic::Resources::Accounts]
2730
attr_reader :accounts
2831

@@ -149,6 +152,8 @@ def api_status(params = {})
149152
#
150153
# @param api_key [String, nil] Defaults to `ENV["LITHIC_API_KEY"]`
151154
#
155+
# @param webhook_secret [String, nil] Defaults to `ENV["LITHIC_WEBHOOK_SECRET"]`
156+
#
152157
# @param environment [:production, :sandbox, nil] Specifies the environment to use for the API.
153158
#
154159
# Each environment maps to a different base URL:
@@ -168,6 +173,7 @@ def api_status(params = {})
168173
# @param max_retry_delay [Float]
169174
def initialize(
170175
api_key: ENV["LITHIC_API_KEY"],
176+
webhook_secret: ENV["LITHIC_WEBHOOK_SECRET"],
171177
environment: nil,
172178
base_url: ENV["LITHIC_BASE_URL"],
173179
max_retries: self.class::DEFAULT_MAX_RETRIES,
@@ -185,6 +191,7 @@ def initialize(
185191
end
186192

187193
@api_key = api_key.to_s
194+
@webhook_secret = webhook_secret&.to_s
188195

189196
super(
190197
base_url: base_url,

lib/lithic/resources/webhooks.rb

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,18 @@ def parse(payload, headers:, secret: nil)
3232
#
3333
# @param payload [String] The raw webhook payload as a string
3434
#
35+
# @param headers [Hash{String=>String}] The raw HTTP headers that came with the payload
36+
#
37+
# @param key [String, nil] The webhook signing key
38+
#
3539
# @return [Lithic::Models::AccountHolderCreatedWebhookEvent, Lithic::Models::ParsedWebhookEvent::KYBPayload, Lithic::Models::ParsedWebhookEvent::KYCPayload, Lithic::Models::ParsedWebhookEvent::LegacyPayload, Lithic::Models::AccountHolderVerificationWebhookEvent, Lithic::Models::AccountHolderDocumentUpdatedWebhookEvent, Lithic::Models::CardAuthorizationApprovalRequestWebhookEvent, Lithic::Models::TokenizationDecisioningRequestWebhookEvent, Lithic::Models::AuthRulesBacktestReportCreatedWebhookEvent, Lithic::Models::BalanceUpdatedWebhookEvent, Lithic::Models::BookTransferTransactionCreatedWebhookEvent, Lithic::Models::BookTransferTransactionUpdatedWebhookEvent, Lithic::Models::CardCreatedWebhookEvent, Lithic::Models::CardConvertedWebhookEvent, Lithic::Models::CardRenewedWebhookEvent, Lithic::Models::CardReissuedWebhookEvent, Lithic::Models::CardShippedWebhookEvent, Lithic::Models::CardUpdatedWebhookEvent, Lithic::Models::CardTransactionUpdatedWebhookEvent, Lithic::Models::CardTransactionEnhancedDataCreatedWebhookEvent, Lithic::Models::CardTransactionEnhancedDataUpdatedWebhookEvent, Lithic::Models::DigitalWalletTokenizationApprovalRequestWebhookEvent, Lithic::Models::DigitalWalletTokenizationResultWebhookEvent, Lithic::Models::DigitalWalletTokenizationTwoFactorAuthenticationCodeWebhookEvent, Lithic::Models::DigitalWalletTokenizationTwoFactorAuthenticationCodeSentWebhookEvent, Lithic::Models::DigitalWalletTokenizationUpdatedWebhookEvent, Lithic::Models::DisputeUpdatedWebhookEvent, Lithic::Models::DisputeEvidenceUploadFailedWebhookEvent, Lithic::Models::ExternalBankAccountCreatedWebhookEvent, Lithic::Models::ExternalBankAccountUpdatedWebhookEvent, Lithic::Models::ExternalPaymentCreatedWebhookEvent, Lithic::Models::ExternalPaymentUpdatedWebhookEvent, Lithic::Models::FinancialAccountCreatedWebhookEvent, Lithic::Models::FinancialAccountUpdatedWebhookEvent, Lithic::Models::FundingEventCreatedWebhookEvent, Lithic::Models::LoanTapeCreatedWebhookEvent, Lithic::Models::LoanTapeUpdatedWebhookEvent, Lithic::Models::ManagementOperationCreatedWebhookEvent, Lithic::Models::ManagementOperationUpdatedWebhookEvent, Lithic::Models::InternalTransactionCreatedWebhookEvent, Lithic::Models::InternalTransactionUpdatedWebhookEvent, Lithic::Models::NetworkTotalCreatedWebhookEvent, Lithic::Models::NetworkTotalUpdatedWebhookEvent, Lithic::Models::PaymentTransactionCreatedWebhookEvent, Lithic::Models::PaymentTransactionUpdatedWebhookEvent, Lithic::Models::SettlementReportUpdatedWebhookEvent, Lithic::Models::StatementsCreatedWebhookEvent, Lithic::Models::ThreeDSAuthenticationCreatedWebhookEvent, Lithic::Models::ThreeDSAuthenticationUpdatedWebhookEvent, Lithic::Models::ThreeDSAuthenticationChallengeWebhookEvent, Lithic::Models::TokenizationApprovalRequestWebhookEvent, Lithic::Models::TokenizationResultWebhookEvent, Lithic::Models::TokenizationTwoFactorAuthenticationCodeWebhookEvent, Lithic::Models::TokenizationTwoFactorAuthenticationCodeSentWebhookEvent, Lithic::Models::TokenizationUpdatedWebhookEvent, Lithic::Models::ThreeDSAuthenticationApprovalRequestWebhookEvent, Lithic::Models::DisputeTransactionCreatedWebhookEvent, Lithic::Models::DisputeTransactionUpdatedWebhookEvent]
36-
def parse_unsafe(payload)
40+
def parse_unsafe(payload, headers:, key: @client.webhook_secret)
41+
if key.nil?
42+
raise ArgumentError.new("Cannot verify a webhook without a key on either the client's webhook_secret or passed in as an argument")
43+
end
44+
45+
::StandardWebhooks::Webhook.new(key).verify(payload, headers)
46+
3747
parsed = JSON.parse(payload, symbolize_names: true)
3848
Lithic::Internal::Type::Converter.coerce(Lithic::Models::ParsedWebhookEvent, parsed)
3949
end

lithic.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,5 @@ Gem::Specification.new do |s|
2626
s.extra_rdoc_files = ["README.md"]
2727
s.add_dependency "cgi"
2828
s.add_dependency "connection_pool"
29+
s.add_dependency "standardwebhooks"
2930
end

rbi/lithic/client.rbi

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ module Lithic
2222
sig { returns(String) }
2323
attr_reader :api_key
2424

25+
sig { returns(T.nilable(String)) }
26+
attr_reader :webhook_secret
27+
2528
sig { returns(Lithic::Resources::Accounts) }
2629
attr_reader :accounts
2730

@@ -142,6 +145,7 @@ module Lithic
142145
sig do
143146
params(
144147
api_key: T.nilable(String),
148+
webhook_secret: T.nilable(String),
145149
environment: T.nilable(T.any(Symbol, String)),
146150
base_url: T.nilable(String),
147151
max_retries: Integer,
@@ -153,6 +157,8 @@ module Lithic
153157
def self.new(
154158
# Defaults to `ENV["LITHIC_API_KEY"]`
155159
api_key: ENV["LITHIC_API_KEY"],
160+
# Defaults to `ENV["LITHIC_WEBHOOK_SECRET"]`
161+
webhook_secret: ENV["LITHIC_WEBHOOK_SECRET"],
156162
# Specifies the environment to use for the API.
157163
#
158164
# Each environment maps to a different base URL:

sig/lithic/client.rbs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ module Lithic
1515

1616
attr_reader api_key: String
1717

18+
attr_reader webhook_secret: String?
19+
1820
attr_reader accounts: Lithic::Resources::Accounts
1921

2022
attr_reader account_holders: Lithic::Resources::AccountHolders
@@ -91,6 +93,7 @@ module Lithic
9193

9294
def initialize: (
9395
?api_key: String?,
96+
?webhook_secret: String?,
9497
?environment: :production | :sandbox | nil,
9598
?base_url: String?,
9699
?max_retries: Integer,

0 commit comments

Comments
 (0)