inttegro 4.1.0 → 4.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 70427e59f95d2e1432182e67f4192df8a3c76b8246afcfa6f5247898f99d6681
4
- data.tar.gz: dd9374452a04136400903a24a80bbd3b6b983571c213540080d2a04b262c4e91
3
+ metadata.gz: 7b60affd2f9bc5ce1556ec7aeeacc26d0d52604d964df3c0ffd72ae034800130
4
+ data.tar.gz: 93520c2c26b96c4292eace71528343cd5ff5f33039e563132c3161d07dce03fe
5
5
  SHA512:
6
- metadata.gz: 64ff092d9abf19ead2714be30d1a05be574b59a5d059d1dea76b2d94303714593ae0b5b0be4f6f1f4c0d640c0aafc87bfac74bbe69bca43ac1247635e725b21b
7
- data.tar.gz: 14df7fb8165c4201be2268c77774f01c3b7d7652ec50c080f506a861655762fbe819a25316292667fc9f1bc2fcaa830f7ebf591c54d97a8d03314c05cd5da5e1
6
+ metadata.gz: 42d25f00a4b8d6ce066f044b2572fd31f3a49aaac7fb98085817673816695b2b69cb83e93e1381c685a5da73236cc9c663e8e493ab3e3118e28698b7e22e1cf9
7
+ data.tar.gz: 22b17729fa018888d51fc33945536a6624298f1b8b66a6a2599b3009fce8a79575e574dc4caefc531535e5812c64b397ce87ebd5f3eff908307890a12934f830
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [4.2.1] - 2026-09-04
4
+
5
+ - Aligned the SDK parity test with hosted-checkout routes that are intentionally handled by the client.
6
+ - Refreshed generated declarations from the OpenAPI commit pinned by CI and release automation.
7
+
8
+ ## [4.2.0] - 2026-09-04
9
+
10
+ - Added vendor-neutral OpenTelemetry spans for logical SDK operations, HTTP attempts, response receipt, decoding, and safe failure categories.
11
+ - Added W3C trace-context propagation plus global or per-client tracer-provider configuration.
12
+ - Kept request bodies, credentials, resource identifiers, dynamic URLs, and exception details out of telemetry.
13
+
3
14
  ## [4.1.0] - 2026-09-03
4
15
 
5
16
  - Added focused `Inttegro::Wallets` and `Inttegro::BankAccounts` modules for financial-account variants.
data/README.md CHANGED
@@ -73,6 +73,20 @@ end
73
73
 
74
74
  Amounts use integer minor units: `5000` GHS is GHS 50.00. Reuse the same idempotency key when retrying the same logical write. If you omit one, the SDK generates a UUIDv7 key for mutating calls.
75
75
 
76
+ ## Observe SDK operations
77
+
78
+ The SDK emits vendor-neutral OpenTelemetry spans through your application's provider. It never configures an exporter or sends telemetry by itself. Configure OpenTelemetry before creating the client:
79
+
80
+ ```ruby
81
+ require "opentelemetry/sdk"
82
+ require "inttegro"
83
+
84
+ OpenTelemetry::SDK.configure
85
+ inttegro = Inttegro::Client.new(api_key: ENV.fetch("INTTEGRO_API_KEY"))
86
+ ```
87
+
88
+ Spans are named after logical operations such as `inttegro.orders.create`. HTTP attempts, response receipt, and decoding are span events. API keys, bodies, resource IDs, dynamic URLs, and exception messages are never recorded. See [SDK observability](https://studio.inttegro.com/sdk-observability) for the complete contract and set `telemetry_enabled: false` when needed.
89
+
76
90
  ## Work with the API
77
91
 
78
92
  The SDK covers orders and checkout, customers, products and prices, purchase intents, payment methods, balances, payouts and refunds, notifications, files, application settings, keys, and country specifications. Resources use snake-case readers such as `purchase_intents` and `payment_methods`.
@@ -95,7 +109,7 @@ The GitHub release for each version is the canonical record. It contains the exa
95
109
 
96
110
  ```bash
97
111
  sha256sum --check SHA256SUMS
98
- gh attestation verify inttegro-4.1.0.gem \
112
+ gh attestation verify inttegro-4.2.1.gem \
99
113
  --repo zebodotdev/inttegro-sdk-ruby
100
114
  ```
101
115
 
@@ -109,7 +109,9 @@ module Inttegro
109
109
  base_url: T.nilable(String),
110
110
  read_timeout: T.nilable(Numeric),
111
111
  open_timeout: T.nilable(Numeric),
112
- adapter: T.nilable(Types::Adapter)
112
+ adapter: T.nilable(Types::Adapter),
113
+ telemetry_enabled: T::Boolean,
114
+ tracer_provider: T.nilable(OpenTelemetry::Trace::TracerProvider)
113
115
  ).void
114
116
  end
115
117
  def initialize(
@@ -119,7 +121,9 @@ module Inttegro
119
121
  base_url: DEFAULT_BASE_URL,
120
122
  read_timeout: 30,
121
123
  open_timeout: 10,
122
- adapter: nil
124
+ adapter: nil,
125
+ telemetry_enabled: true,
126
+ tracer_provider: nil
123
127
  )
124
128
  provided_token = token || token_value
125
129
  api_key ||= provided_token
@@ -128,7 +132,9 @@ module Inttegro
128
132
  base_url: base_url,
129
133
  read_timeout: read_timeout,
130
134
  open_timeout: open_timeout,
131
- adapter: adapter
135
+ adapter: adapter,
136
+ telemetry_enabled: telemetry_enabled,
137
+ tracer_provider: tracer_provider
132
138
  ), HTTPClient)
133
139
 
134
140
  @orders = T.let(Resources::Orders.new(@http), Resources::Orders)
@@ -47,6 +47,50 @@ module Inttegro
47
47
  end
48
48
  end
49
49
 
50
+ class CheckoutInvoiceViewStatus < T::Enum
51
+ enums do
52
+ DRAFT = new("draft")
53
+ REQUIRES_PAYMENT = new("requires_payment")
54
+ OVERDUE = new("overdue")
55
+ PAID = new("paid")
56
+ EXPIRED = new("expired")
57
+ CANCELED = new("canceled")
58
+ end
59
+ end
60
+
61
+ class CheckoutMobileMoneyDataNetwork < T::Enum
62
+ enums do
63
+ MTN = new("mtn")
64
+ VODAFONE = new("vodafone")
65
+ AIRTEL = new("airtel")
66
+ TELECEL = new("telecel")
67
+ end
68
+ end
69
+
70
+ class CheckoutOrderStatus < T::Enum
71
+ enums do
72
+ PREPARING = new("preparing")
73
+ REQUIRES_PAYMENT = new("requires_payment")
74
+ COMPLETED = new("completed")
75
+ PAID = new("paid")
76
+ CANCELED = new("canceled")
77
+ EXPIRED = new("expired")
78
+ UNKNOWN = new("unknown")
79
+ end
80
+ end
81
+
82
+ class CheckoutPayRequestPaymentMethodDataType < T::Enum
83
+ enums do
84
+ MOBILE_MONEY = new("mobile_money")
85
+ end
86
+ end
87
+
88
+ class CheckoutPaymentMethodDataType < T::Enum
89
+ enums do
90
+ MOBILE_MONEY = new("mobile_money")
91
+ end
92
+ end
93
+
50
94
  class ChimeEmailSchemaKind < T::Enum
51
95
  enums do
52
96
  GMAIL_VIEW_ACTION = new("gmail_view_action")
@@ -562,6 +562,256 @@ module Inttegro
562
562
  const :quantity, Integer
563
563
  end
564
564
 
565
+ class CheckoutPaymentAddress < T::Struct
566
+ const :name, T.nilable(String), default: nil
567
+ const :phone_number, T.nilable(String), default: nil
568
+ const :line1, T.nilable(String), default: nil
569
+ const :line2, T.nilable(String), default: nil
570
+ const :city, T.nilable(String), default: nil
571
+ const :region, T.nilable(String), default: nil
572
+ const :post_code, T.nilable(String), default: nil
573
+ const :country, String
574
+ end
575
+
576
+ class CheckoutBillingDetails < T::Struct
577
+ const :name, String
578
+ const :phone_number, T.nilable(String), default: nil
579
+ const :address, Inttegro::CheckoutPaymentAddress
580
+ end
581
+
582
+ class CheckoutConfirmPaymentRequest < T::Struct
583
+ const :order_id, String
584
+ const :payment_id, T.nilable(String), default: nil
585
+ const :confirmation_id, T.nilable(String), default: nil
586
+ const :token, String
587
+ end
588
+
589
+ class CheckoutInvoiceViewFormatWeb < T::Struct
590
+ const :url, T.nilable(String), default: nil
591
+ end
592
+
593
+ class CheckoutInvoiceViewFormatPdf < T::Struct
594
+ const :url, T.nilable(String), default: nil
595
+ end
596
+
597
+ class CheckoutInvoiceViewFormatReceipt < T::Struct
598
+ const :url, T.nilable(String), default: nil
599
+ end
600
+
601
+ class CheckoutInvoiceViewFormat < T::Struct
602
+ const :web, T.nilable(Inttegro::CheckoutInvoiceViewFormatWeb), default: nil
603
+ const :pdf, T.nilable(Inttegro::CheckoutInvoiceViewFormatPdf), default: nil
604
+ const :receipt, T.nilable(Inttegro::CheckoutInvoiceViewFormatReceipt), default: nil
605
+ end
606
+
607
+ class CheckoutInvoiceViewBeneficiary < T::Struct
608
+ const :id, T.nilable(String), default: nil
609
+ const :legal_entity_type, T.nilable(String), default: nil
610
+ const :logo_url, T.nilable(String), default: nil
611
+ const :alias, T.nilable(String), default: nil
612
+ const :name, T.nilable(String), default: nil
613
+ const :support_email, T.nilable(String), default: nil
614
+ const :support_phone, T.nilable(String), default: nil
615
+ const :website_url, T.nilable(String), default: nil
616
+ const :accent_color, T.nilable(String), default: nil
617
+ const :invoice_support_line, T.nilable(String), default: nil
618
+ const :invoice_footer, T.nilable(String), default: nil
619
+ end
620
+
621
+ class CheckoutInvoiceView < T::Struct
622
+ const :id, String
623
+ const :application_id, T.nilable(String), default: nil
624
+ const :number, T.nilable(String), default: nil
625
+ const :status, T.nilable(Inttegro::CheckoutInvoiceViewStatus), default: nil
626
+ const :created_at, String
627
+ const :due_at, T.nilable(String), default: nil
628
+ const :format_value, Inttegro::CheckoutInvoiceViewFormat, name: "format"
629
+ const :beneficiary, T.nilable(Inttegro::CheckoutInvoiceViewBeneficiary), default: nil
630
+ end
631
+
632
+ class CheckoutMobileMoneyData < T::Struct
633
+ const :reference, T.nilable(String), default: nil
634
+ const :network, Inttegro::CheckoutMobileMoneyDataNetwork
635
+ const :account_number, String
636
+ end
637
+
638
+ class CheckoutOrderCheckoutSettings < T::Struct
639
+ const :redirect_url, T.nilable(String), default: nil
640
+ const :cancel_url, T.nilable(String), default: nil
641
+ end
642
+
643
+ class InlineProductDetails < T::Struct
644
+ const :about, T.nilable(String), default: nil
645
+ const :custom_data, T.nilable(T::Hash[String, Object]), default: nil
646
+ const :name, String
647
+ const :price, Inttegro::PriceParams
648
+ const :quantity, Integer
649
+ const :reference, T.nilable(String), default: nil
650
+ const :tax_code, T.nilable(String), default: nil
651
+ const :type, Inttegro::ProductType
652
+ end
653
+
654
+ ProductDetails = T.type_alias { T.any(Inttegro::InlineProductDetails, Inttegro::CatalogProductWithPriceData, Inttegro::CatalogProductWithPriceReference) }
655
+
656
+ class ProductLineItem < T::Struct
657
+ const :type, Inttegro::ProductLineItemType
658
+ const :product, Inttegro::ProductDetails
659
+ end
660
+
661
+ class FeeDetails < T::Struct
662
+ const :id, T.nilable(String), default: nil
663
+ const :label, T.nilable(String), default: nil
664
+ const :tax_code, T.nilable(String), default: nil
665
+ const :description, T.nilable(String), default: nil
666
+ const :custom_data, T.nilable(T::Hash[String, Object]), default: nil
667
+ const :amount, Inttegro::AmountParams
668
+ end
669
+
670
+ class FeeLineItem < T::Struct
671
+ const :type, Inttegro::FeeLineItemType
672
+ const :fee, Inttegro::FeeDetails
673
+ end
674
+
675
+ class ShippingDetails < T::Struct
676
+ const :id, T.nilable(String), default: nil
677
+ const :tax_code, T.nilable(String), default: nil
678
+ const :custom_data, T.nilable(T::Hash[String, Object]), default: nil
679
+ const :fee, Inttegro::AmountParams
680
+ end
681
+
682
+ class ShippingLineItem < T::Struct
683
+ const :type, Inttegro::ShippingLineItemType
684
+ const :shipping, Inttegro::ShippingDetails
685
+ end
686
+
687
+ LineItem = T.type_alias { T.any(Inttegro::ProductLineItem, Inttegro::FeeLineItem, Inttegro::ShippingLineItem) }
688
+
689
+ class CheckoutOrderLineItemGroup < T::Struct
690
+ const :line_items, T.nilable(T::Array[Inttegro::LineItem]), default: nil
691
+ const :total, T.nilable(Inttegro::Amount), default: nil
692
+ end
693
+
694
+ class CheckoutOrderCustomerBillingAddress < T::Struct
695
+ # This object intentionally has no fields.
696
+ end
697
+
698
+ class CheckoutOrderCustomerShippingAddress < T::Struct
699
+ # This object intentionally has no fields.
700
+ end
701
+
702
+ class CheckoutOrderCustomer < T::Struct
703
+ const :id, T.nilable(String), default: nil
704
+ const :guest, T.nilable(T::Boolean), default: nil
705
+ const :name, T.nilable(String), default: nil
706
+ const :email_address, T.nilable(String), default: nil
707
+ const :phone_number, T.nilable(String), default: nil
708
+ const :billing_address, T.nilable(Inttegro::CheckoutOrderCustomerBillingAddress), default: nil
709
+ const :shipping_address, T.nilable(Inttegro::CheckoutOrderCustomerShippingAddress), default: nil
710
+ end
711
+
712
+ class CheckoutOrderCreatedFrom < T::Struct
713
+ const :source, T.nilable(String), default: nil
714
+ const :resource_type, T.nilable(String), default: nil
715
+ const :resource_id, T.nilable(String), default: nil
716
+ end
717
+
718
+ class CheckoutOrderPaymentPaymentMethod < T::Struct
719
+ # This object intentionally has no fields.
720
+ end
721
+
722
+ class CheckoutOrderPaymentBillingDetails < T::Struct
723
+ # This object intentionally has no fields.
724
+ end
725
+
726
+ class CheckoutOrderPaymentLatestAttempt < T::Struct
727
+ # This object intentionally has no fields.
728
+ end
729
+
730
+ class CheckoutOrderPaymentNextAction < T::Struct
731
+ # This object intentionally has no fields.
732
+ end
733
+
734
+ class CheckoutOrderPaymentLatestError < T::Struct
735
+ # This object intentionally has no fields.
736
+ end
737
+
738
+ class CheckoutOrderPayment < T::Struct
739
+ const :id, T.nilable(String), default: nil
740
+ const :statement_descriptor, T.nilable(String), default: nil
741
+ const :payment_method_types, T.nilable(T::Array[String]), default: nil
742
+ const :payment_method, T.nilable(Inttegro::CheckoutOrderPaymentPaymentMethod), default: nil
743
+ const :billing_details, T.nilable(Inttegro::CheckoutOrderPaymentBillingDetails), default: nil
744
+ const :receipt, T.nilable(Inttegro::CheckoutInvoiceView), default: nil
745
+ const :latest_attempt, T.nilable(Inttegro::CheckoutOrderPaymentLatestAttempt), default: nil
746
+ const :amount, T.nilable(Inttegro::Amount), default: nil
747
+ const :next_action, T.nilable(Inttegro::CheckoutOrderPaymentNextAction), default: nil
748
+ const :latest_error, T.nilable(Inttegro::CheckoutOrderPaymentLatestError), default: nil
749
+ const :status, T.nilable(String), default: nil
750
+ const :initiated_at, T.nilable(String), default: nil
751
+ const :executed_at, T.nilable(String), default: nil
752
+ const :due_at, T.nilable(String), default: nil
753
+ const :canceled_at, T.nilable(String), default: nil
754
+ const :expired_at, T.nilable(String), default: nil
755
+ const :paid_at, T.nilable(String), default: nil
756
+ const :paid_offline, T.nilable(T::Boolean), default: nil
757
+ const :failed_at, T.nilable(String), default: nil
758
+ end
759
+
760
+ class CheckoutOrder < T::Struct
761
+ const :id, String
762
+ const :number, T.nilable(String), default: nil
763
+ const :receipt_number, T.nilable(String), default: nil
764
+ const :reference, T.nilable(String), default: nil
765
+ const :status, Inttegro::CheckoutOrderStatus
766
+ const :initiated_at, String
767
+ const :sealed_at, T.nilable(String), default: nil
768
+ const :completed_at, T.nilable(String), default: nil
769
+ const :paid_at, T.nilable(String), default: nil
770
+ const :canceled_at, T.nilable(String), default: nil
771
+ const :expires_at, T.nilable(String), default: nil
772
+ const :payment_due_at, T.nilable(String), default: nil
773
+ const :checkout_settings, T.nilable(Inttegro::CheckoutOrderCheckoutSettings), default: nil
774
+ const :line_item_group, T.nilable(Inttegro::CheckoutOrderLineItemGroup), default: nil
775
+ const :customer, Inttegro::CheckoutOrderCustomer
776
+ const :created_from, T.nilable(Inttegro::CheckoutOrderCreatedFrom), default: nil
777
+ const :payment, T.nilable(Inttegro::CheckoutOrderPayment), default: nil
778
+ const :invoice, T.nilable(Inttegro::CheckoutInvoiceView), default: nil
779
+ end
780
+
781
+ class CheckoutOrderReferenceRequest < T::Struct
782
+ const :order_id, String
783
+ end
784
+
785
+ class CheckoutOrderResponse < T::Struct
786
+ const :order, T.nilable(Inttegro::CheckoutOrder), default: nil
787
+ const :error, T.nilable(Inttegro::ErrorPayload), default: nil
788
+ end
789
+
790
+ class CheckoutPaymentMethodDataBillingDetails < T::Struct
791
+ const :name, String
792
+ const :phone_number, T.nilable(String), default: nil
793
+ const :address, Inttegro::CheckoutPaymentAddress
794
+ end
795
+
796
+ class CheckoutPayRequestPaymentMethodData < T::Struct
797
+ const :type, Inttegro::CheckoutPayRequestPaymentMethodDataType
798
+ const :mobile_money, Inttegro::CheckoutMobileMoneyData
799
+ const :billing_details, T.nilable(Inttegro::CheckoutPaymentMethodDataBillingDetails), default: nil
800
+ end
801
+
802
+ class CheckoutPayRequest < T::Struct
803
+ const :order_id, String
804
+ const :payment_method_id, T.nilable(String), default: nil
805
+ const :payment_method_data, T.nilable(Inttegro::CheckoutPayRequestPaymentMethodData), default: nil
806
+ const :save_payment_method, T.nilable(T::Boolean), default: nil
807
+ end
808
+
809
+ class CheckoutPaymentMethodData < T::Struct
810
+ const :type, Inttegro::CheckoutPaymentMethodDataType
811
+ const :mobile_money, Inttegro::CheckoutMobileMoneyData
812
+ const :billing_details, T.nilable(Inttegro::CheckoutPaymentMethodDataBillingDetails), default: nil
813
+ end
814
+
565
815
  class ChimeRecipientDetailPhone < T::Struct
566
816
  const :number, String
567
817
  end
@@ -1358,52 +1608,6 @@ module Inttegro
1358
1608
  const :enable_fx, T.nilable(T::Boolean), default: nil
1359
1609
  end
1360
1610
 
1361
- class InlineProductDetails < T::Struct
1362
- const :about, T.nilable(String), default: nil
1363
- const :custom_data, T.nilable(T::Hash[String, Object]), default: nil
1364
- const :name, String
1365
- const :price, Inttegro::PriceParams
1366
- const :quantity, Integer
1367
- const :reference, T.nilable(String), default: nil
1368
- const :tax_code, T.nilable(String), default: nil
1369
- const :type, Inttegro::ProductType
1370
- end
1371
-
1372
- ProductDetails = T.type_alias { T.any(Inttegro::InlineProductDetails, Inttegro::CatalogProductWithPriceData, Inttegro::CatalogProductWithPriceReference) }
1373
-
1374
- class ProductLineItem < T::Struct
1375
- const :type, Inttegro::ProductLineItemType
1376
- const :product, Inttegro::ProductDetails
1377
- end
1378
-
1379
- class FeeDetails < T::Struct
1380
- const :id, T.nilable(String), default: nil
1381
- const :label, T.nilable(String), default: nil
1382
- const :tax_code, T.nilable(String), default: nil
1383
- const :description, T.nilable(String), default: nil
1384
- const :custom_data, T.nilable(T::Hash[String, Object]), default: nil
1385
- const :amount, Inttegro::AmountParams
1386
- end
1387
-
1388
- class FeeLineItem < T::Struct
1389
- const :type, Inttegro::FeeLineItemType
1390
- const :fee, Inttegro::FeeDetails
1391
- end
1392
-
1393
- class ShippingDetails < T::Struct
1394
- const :id, T.nilable(String), default: nil
1395
- const :tax_code, T.nilable(String), default: nil
1396
- const :custom_data, T.nilable(T::Hash[String, Object]), default: nil
1397
- const :fee, Inttegro::AmountParams
1398
- end
1399
-
1400
- class ShippingLineItem < T::Struct
1401
- const :type, Inttegro::ShippingLineItemType
1402
- const :shipping, Inttegro::ShippingDetails
1403
- end
1404
-
1405
- LineItem = T.type_alias { T.any(Inttegro::ProductLineItem, Inttegro::FeeLineItem, Inttegro::ShippingLineItem) }
1406
-
1407
1611
  class Shipping < T::Struct
1408
1612
  const :address, Inttegro::Address
1409
1613
  end
@@ -31,6 +31,10 @@ module Inttegro
31
31
  "/customers/lookup" => Inttegro::CustomerResponse,
32
32
  "/customers/update" => Inttegro::CustomerResponse,
33
33
  "/customers/page" => Inttegro::PageCustomersResponse,
34
+ "/checkout/lookup" => Inttegro::CheckoutOrderResponse,
35
+ "/checkout/pay" => Inttegro::CheckoutOrderResponse,
36
+ "/checkout/confirm_payment" => Inttegro::CheckoutOrderResponse,
37
+ "/checkout/request_confirmation" => Inttegro::CheckoutOrderResponse,
34
38
  "/orders/create" => Inttegro::OrderEnvelope,
35
39
  "/orders/lookup" => Inttegro::OrderEnvelope,
36
40
  "/orders/update" => Inttegro::OrderEnvelope,
@@ -150,6 +154,10 @@ module Inttegro
150
154
  "/customers/lookup" => "Inttegro::LookupCustomerRequest",
151
155
  "/customers/update" => "Inttegro::UpdateCustomerRequest",
152
156
  "/customers/page" => "Inttegro::PageCustomersRequest",
157
+ "/checkout/lookup" => "Inttegro::CheckoutOrderReferenceRequest",
158
+ "/checkout/pay" => "Inttegro::CheckoutPayRequest",
159
+ "/checkout/confirm_payment" => "Inttegro::CheckoutConfirmPaymentRequest",
160
+ "/checkout/request_confirmation" => "Inttegro::CheckoutOrderReferenceRequest",
153
161
  "/orders/create" => "T.any(Inttegro::CreateOrderNewCustomer, Inttegro::CreateOrderExistingCustomer)",
154
162
  "/orders/lookup" => "Inttegro::LookupOrderRequest",
155
163
  "/orders/update" => "Inttegro::UpdateOrderRequest",
@@ -267,6 +275,10 @@ module Inttegro
267
275
  "/customers/lookup" => "lookupCustomer",
268
276
  "/customers/update" => "updateCustomer",
269
277
  "/customers/page" => "pageCustomers",
278
+ "/checkout/lookup" => "lookupCheckoutOrder",
279
+ "/checkout/pay" => "payCheckoutOrder",
280
+ "/checkout/confirm_payment" => "confirmCheckoutPayment",
281
+ "/checkout/request_confirmation" => "requestCheckoutConfirmation",
270
282
  "/orders/create" => "createOrder",
271
283
  "/orders/lookup" => "lookupOrder",
272
284
  "/orders/update" => "updateOrder",
@@ -12,6 +12,7 @@ require_relative "models"
12
12
  require_relative "generated/operations"
13
13
  require_relative "response_object"
14
14
  require_relative "transport_response"
15
+ require_relative "telemetry"
15
16
  require_relative "types"
16
17
  require_relative "version"
17
18
 
@@ -34,10 +35,20 @@ module Inttegro
34
35
  base_url: T.nilable(String),
35
36
  read_timeout: T.nilable(Numeric),
36
37
  open_timeout: T.nilable(Numeric),
37
- adapter: T.nilable(Types::Adapter)
38
+ adapter: T.nilable(Types::Adapter),
39
+ telemetry_enabled: T::Boolean,
40
+ tracer_provider: T.nilable(OpenTelemetry::Trace::TracerProvider)
38
41
  ).void
39
42
  end
40
- def initialize(api_key:, base_url: DEFAULT_BASE_URL, read_timeout: 30, open_timeout: 10, adapter: nil)
43
+ def initialize(
44
+ api_key:,
45
+ base_url: DEFAULT_BASE_URL,
46
+ read_timeout: 30,
47
+ open_timeout: 10,
48
+ adapter: nil,
49
+ telemetry_enabled: true,
50
+ tracer_provider: nil
51
+ )
41
52
  @api_key = T.let(api_key || "", String)
42
53
  raise ArgumentError, "api_key is required" if @api_key.strip.empty?
43
54
 
@@ -48,6 +59,10 @@ module Inttegro
48
59
  @read_timeout = T.let(read_timeout, T.nilable(Numeric))
49
60
  @open_timeout = T.let(open_timeout, T.nilable(Numeric))
50
61
  @adapter = T.let(adapter, T.nilable(Types::Adapter))
62
+ @telemetry = T.let(
63
+ Telemetry.new(Inttegro::VERSION, enabled: telemetry_enabled, tracer_provider: tracer_provider),
64
+ Telemetry
65
+ )
51
66
  end
52
67
 
53
68
  sig { params(path: String, headers: Types::Headers, query: T.nilable(Types::Query)).returns(ResponseValue) }
@@ -165,16 +180,17 @@ module Inttegro
165
180
  fields: Types::Payload,
166
181
  files: Types::Payload,
167
182
  headers: Types::Headers,
168
- authenticated: T::Boolean
183
+ authenticated: T::Boolean,
184
+ operation: T.nilable(String)
169
185
  )
170
186
  .returns(T.type_parameter(:Model))
171
187
  end
172
- def post_multipart_model(path, model, fields: {}, files: {}, headers: {}, authenticated: true)
188
+ def post_multipart_model(path, model, fields: {}, files: {}, headers: {}, authenticated: true, operation: nil)
173
189
  unless model <= T::Struct
174
190
  raise TypeError, "response model for #{path} must inherit from T::Struct"
175
191
  end
176
192
 
177
- response = perform_multipart(path, fields, files, headers, authenticated, model)
193
+ response = perform_multipart(path, fields, files, headers, authenticated, model, operation)
178
194
  return T.cast(response, T.type_parameter(:Model)) if response.is_a?(model)
179
195
 
180
196
  raise TypeError, "expected #{model} from #{path}, got #{response.class}"
@@ -218,62 +234,76 @@ module Inttegro
218
234
  files: Types::Payload,
219
235
  headers: Types::Headers,
220
236
  authenticated: T::Boolean,
221
- model: T.nilable(T::Class[T::Struct])
237
+ model: T.nilable(T::Class[T::Struct]),
238
+ operation: T.nilable(String)
222
239
  ).returns(ResponseValue)
223
240
  end
224
- def perform_multipart(path, fields, files, headers, authenticated, model)
225
- uri = build_uri(path, nil)
226
- request = Net::HTTP::Post.new(uri)
227
- request["Accept"] = "application/json"
228
- request["User-Agent"] = USER_AGENT
229
- request["Authorization"] = "Bearer #{@api_key}" if authenticated
230
- headers = headers.dup
231
- if authenticated && idempotent_mutation_path?(path) && !header_present?(headers, "Idempotency-Key")
232
- headers["Idempotency-Key"] = generate_idempotency_key
233
- end
234
- headers.each { |key, value| request[key] = value }
241
+ def perform_multipart(path, fields, files, headers, authenticated, model, operation = nil)
242
+ @telemetry.in_span(path, :post, @base_url, Inttegro::VERSION, operation) do |span|
243
+ uri = build_uri(path, nil)
244
+ request = Net::HTTP::Post.new(uri)
245
+ request["Accept"] = "application/json"
246
+ request["User-Agent"] = USER_AGENT
247
+ request["Authorization"] = "Bearer #{@api_key}" if authenticated
248
+ headers = headers.dup
249
+ if authenticated && idempotent_mutation_path?(path) && !header_present?(headers, "Idempotency-Key")
250
+ headers["Idempotency-Key"] = generate_idempotency_key
251
+ end
252
+ headers.each { |key, value| request[key] = value }
235
253
 
236
- form = []
237
- fields.each do |key, value|
238
- next if value.nil?
254
+ form = []
255
+ fields.each do |key, value|
256
+ next if value.nil?
239
257
 
240
- form << [key.to_s, value.is_a?(Hash) || value.is_a?(Array) ? JSON.generate(value) : value.to_s]
241
- end
242
- files.each do |key, path|
243
- raise ArgumentError, "file path for #{key} must be a String" unless path.is_a?(String)
258
+ form << [key.to_s, value.is_a?(Hash) || value.is_a?(Array) ? JSON.generate(value) : value.to_s]
259
+ end
260
+ files.each do |key, file_path|
261
+ raise ArgumentError, "file path for #{key} must be a String" unless file_path.is_a?(String)
244
262
 
245
- form << [key.to_s, File.open(path)]
263
+ form << [key.to_s, File.open(file_path)]
264
+ end
265
+ request.set_form(form, "multipart/form-data")
266
+ @telemetry.prepare(span, request)
267
+
268
+ raw_response = @adapter ? @adapter.call(uri, request) : build_http(uri).request(request)
269
+ response = TransportResponse.new(raw_response)
270
+ @telemetry.response(span, response, decoded: false)
271
+ result = handle_response(path, response, model)
272
+ @telemetry.decoded(span)
273
+ result
246
274
  end
247
- request.set_form(form, "multipart/form-data")
248
-
249
- raw_response = @adapter ? @adapter.call(uri, request) : build_http(uri).request(request)
250
- response = TransportResponse.new(raw_response)
251
- handle_response(path, response, model)
252
275
  end
253
276
 
254
277
  sig { params(path: String, body: Types::RequestBody).returns(FileDownload) }
255
278
  def post_binary_json(path, body)
256
- body = with_request_meta_idempotency(:post, path, coerce_body(body), {})
257
- response = raw_request(:post, path, JSON.dump(body), {
258
- "Accept" => "application/octet-stream",
259
- "Content-Type" => "application/json",
260
- "Authorization" => "Bearer #{@api_key}"
261
- })
262
- return FileDownload.new(response.body, response.headers) if response.code.to_i < 400
263
-
264
- raise_response_error(response)
279
+ @telemetry.in_span(path, :post, @base_url, Inttegro::VERSION) do |span|
280
+ payload = with_request_meta_idempotency(:post, path, coerce_body(body), {})
281
+ response = raw_request(:post, path, JSON.dump(payload), {
282
+ "Accept" => "application/octet-stream",
283
+ "Content-Type" => "application/json",
284
+ "Authorization" => "Bearer #{@api_key}"
285
+ }, span)
286
+ @telemetry.response(span, response, decoded: false)
287
+ return FileDownload.new(response.body, response.headers) if response.code.to_i < 400
288
+
289
+ raise_response_error(response)
290
+ end
265
291
  end
266
292
 
267
293
  sig { params(url: String).returns(FileDownload) }
268
294
  def get_binary_public(url)
269
- uri = build_uri(url, nil)
270
- request = Net::HTTP::Get.new(uri)
271
- request["User-Agent"] = USER_AGENT
272
- raw_response = @adapter ? @adapter.call(uri, request) : build_http(uri).request(request)
273
- response = TransportResponse.new(raw_response)
274
- return FileDownload.new(response.body, response.headers) if response.code.to_i < 400
275
-
276
- raise_response_error(response)
295
+ @telemetry.in_span(url, :get, @base_url, Inttegro::VERSION, "file_links.download") do |span|
296
+ uri = build_uri(url, nil)
297
+ request = Net::HTTP::Get.new(uri)
298
+ request["User-Agent"] = USER_AGENT
299
+ @telemetry.prepare(span, request)
300
+ raw_response = @adapter ? @adapter.call(uri, request) : build_http(uri).request(request)
301
+ response = TransportResponse.new(raw_response)
302
+ @telemetry.response(span, response, decoded: false)
303
+ return FileDownload.new(response.body, response.headers) if response.code.to_i < 400
304
+
305
+ raise_response_error(response)
306
+ end
277
307
  end
278
308
 
279
309
  sig do
@@ -287,20 +317,26 @@ module Inttegro
287
317
  ).returns(ResponseValue)
288
318
  end
289
319
  def request(method, path, body: nil, headers: {}, query: nil, response_model: nil)
290
- uri = build_uri(path, query)
291
- body = validate_body(path, coerce_body(body))
292
- body = with_request_meta_idempotency(method, path, body, headers)
293
- request = build_request(method, uri, body, headers)
294
-
295
- raw_response =
296
- if @adapter
297
- @adapter.call(uri, request)
298
- else
299
- http = build_http(uri)
300
- http.request(request)
301
- end
302
- response = TransportResponse.new(raw_response)
303
- handle_response(path, response, response_model)
320
+ @telemetry.in_span(path, method, @base_url, Inttegro::VERSION) do |span|
321
+ uri = build_uri(path, query)
322
+ body = validate_body(path, coerce_body(body))
323
+ body = with_request_meta_idempotency(method, path, body, headers)
324
+ request = build_request(method, uri, body, headers)
325
+ @telemetry.prepare(span, request)
326
+
327
+ raw_response =
328
+ if @adapter
329
+ @adapter.call(uri, request)
330
+ else
331
+ http = build_http(uri)
332
+ http.request(request)
333
+ end
334
+ response = TransportResponse.new(raw_response)
335
+ @telemetry.response(span, response, decoded: false)
336
+ result = handle_response(path, response, response_model)
337
+ @telemetry.decoded(span)
338
+ result
339
+ end
304
340
  rescue Timeout::Error, Errno::ETIMEDOUT => e
305
341
  raise TimeoutError.new("Request timed out", e)
306
342
  rescue SocketError, Errno::ECONNREFUSED, Errno::ECONNRESET => e
@@ -327,13 +363,15 @@ module Inttegro
327
363
  method: T.any(String, Symbol),
328
364
  path: String,
329
365
  body: T.nilable(String),
330
- headers: Types::Headers
366
+ headers: Types::Headers,
367
+ span: T.nilable(OpenTelemetry::Trace::Span)
331
368
  ).returns(TransportResponse)
332
369
  end
333
- def raw_request(method, path, body, headers)
370
+ def raw_request(method, path, body, headers, span = nil)
334
371
  uri = build_uri(path, nil)
335
372
  request = build_request(method, uri, nil, headers)
336
373
  request.body = body if body
374
+ @telemetry.prepare(span, request)
337
375
  raw_response = @adapter ? @adapter.call(uri, request) : build_http(uri).request(request)
338
376
  TransportResponse.new(raw_response)
339
377
  end
@@ -52,7 +52,8 @@ module Inttegro
52
52
  upload_url,
53
53
  Inttegro::UploadFulfillment,
54
54
  files: { file: file },
55
- authenticated: false
55
+ authenticated: false,
56
+ operation: "upload_requests.upload"
56
57
  )
57
58
  end
58
59
 
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ require "opentelemetry-api"
5
+ require "sorbet-runtime"
6
+ require "uri"
7
+
8
+ require_relative "errors"
9
+
10
+ module Inttegro
11
+ # Emits redacted SDK spans to the application's OpenTelemetry provider.
12
+ # This class never configures an exporter or sends telemetry to Inttegro.
13
+ class Telemetry
14
+ extend T::Sig
15
+
16
+ SAFE_RESOURCES = %w[
17
+ apps balance_transactions balances broadcasts checkout chimes customers file_links file_references files
18
+ financial_accounts keys message_templates orders otp payment_methods payouts ping prices products
19
+ purchase_intents refunds schedules sessions spec upload_requests
20
+ ].freeze
21
+ SAFE_ACTIONS = %w[
22
+ activate add_price archive broadcast cancel complete confirm_payment confirm_verification connect contents
23
+ countries create deactivate delete destroy disable disable_fx disable_pull disable_push disactivate disconnect
24
+ enable enable_fx enable_pull enable_push finalize generate initiate lookup new open page pay publish reconcile
25
+ reconnect refund render_preview request_confirmation review revoke schedule send send_invoice send_receipt
26
+ set_default_unit_price set_destinations settings tokenize unarchive unpublish update upload usage verify
27
+ ].freeze
28
+
29
+ sig do
30
+ params(
31
+ version: String,
32
+ enabled: T::Boolean,
33
+ tracer_provider: T.nilable(OpenTelemetry::Trace::TracerProvider)
34
+ ).void
35
+ end
36
+ def initialize(version, enabled: true, tracer_provider: nil)
37
+ @enabled = T.let(enabled, T::Boolean)
38
+ provider = tracer_provider || OpenTelemetry.tracer_provider
39
+ @tracer = T.let(provider.tracer("inttegro", version), OpenTelemetry::Trace::Tracer)
40
+ end
41
+
42
+ sig do
43
+ type_parameters(:Result)
44
+ .params(
45
+ path_or_url: String,
46
+ method: T.any(String, Symbol),
47
+ base_url: String,
48
+ version: String,
49
+ operation_override: T.nilable(String),
50
+ block: T.proc.params(span: T.nilable(OpenTelemetry::Trace::Span)).returns(T.type_parameter(:Result))
51
+ )
52
+ .returns(T.type_parameter(:Result))
53
+ end
54
+ def in_span(path_or_url, method, base_url, version, operation_override = nil, &block)
55
+ return yield(nil) unless @enabled
56
+
57
+ operation, route, server_address = request_details(path_or_url, base_url, operation_override)
58
+ attributes = {
59
+ "inttegro.operation.name" => operation,
60
+ "inttegro.sdk.language" => "ruby",
61
+ "inttegro.sdk.version" => version,
62
+ "http.request.method" => method.to_s.upcase,
63
+ "server.address" => server_address
64
+ }
65
+ attributes["url.template"] = route if route
66
+ span = @tracer.start_span("inttegro.#{operation}", kind: :client, attributes: attributes)
67
+
68
+ begin
69
+ OpenTelemetry::Trace.with_span(span) { |_active_span, _context| yield(span) }
70
+ rescue StandardError => e
71
+ error_type = classify_error(e)
72
+ span.set_attribute("error.type", error_type)
73
+ span.status = OpenTelemetry::Trace::Status.error
74
+ span.add_event("inttegro.request.failed", attributes: { "error.type" => error_type })
75
+ raise
76
+ ensure
77
+ span.finish
78
+ end
79
+ end
80
+
81
+ sig { params(span: T.nilable(OpenTelemetry::Trace::Span), request: Net::HTTPRequest).void }
82
+ def prepare(span, request)
83
+ if @enabled
84
+ carrier = T.let({}, T::Hash[String, String])
85
+ OpenTelemetry.propagation.inject(carrier)
86
+ carrier.each { |key, value| request[key] = value unless request[key] }
87
+ end
88
+ return unless span
89
+
90
+ span.add_event("inttegro.request.prepared")
91
+ span.add_event(
92
+ "inttegro.http.attempt.started",
93
+ attributes: { "http.request.resend_count" => 0 }
94
+ )
95
+ end
96
+
97
+ sig do
98
+ params(
99
+ span: T.nilable(OpenTelemetry::Trace::Span),
100
+ response: TransportResponse,
101
+ decoded: T::Boolean
102
+ ).void
103
+ end
104
+ def response(span, response, decoded:)
105
+ return unless span
106
+
107
+ status = response.code.to_i
108
+ span.set_attribute("http.response.status_code", status)
109
+ request_id = response["x-request-id"]
110
+ span.set_attribute("inttegro.request.id", request_id) if request_id && !request_id.empty?
111
+ span.add_event(
112
+ "inttegro.response.received",
113
+ attributes: {
114
+ "http.response.status_code" => status,
115
+ "http.request.resend_count" => 0
116
+ }
117
+ )
118
+ span.add_event("inttegro.response.decoded") if decoded
119
+ end
120
+
121
+ sig { params(span: T.nilable(OpenTelemetry::Trace::Span)).void }
122
+ def decoded(span)
123
+ span.add_event("inttegro.response.decoded") if span
124
+ end
125
+
126
+ private
127
+
128
+ sig do
129
+ params(path_or_url: String, base_url: String, override: T.nilable(String))
130
+ .returns([String, T.nilable(String), String])
131
+ end
132
+ def request_details(path_or_url, base_url, override)
133
+ uri = path_or_url.start_with?("http://", "https://") ? URI.parse(path_or_url) : URI.join("#{base_url}/", path_or_url.sub(%r{\A/}, ""))
134
+ segments = path_or_url.start_with?("http://", "https://") ? [] : T.must(uri.path).split("/").reject(&:empty?)
135
+ resource = segments[0]
136
+ action = segments[1]
137
+ known_route = !resource.nil? && segments.length <= 2 && SAFE_RESOURCES.include?(resource) &&
138
+ (action.nil? || SAFE_ACTIONS.include?(action))
139
+ route = known_route ? uri.path : nil
140
+ derived_operation = known_route ? "#{resource}.#{action || (resource == 'balances' ? 'lookup' : 'request')}" : "http.request"
141
+ [override || derived_operation, route, uri.host || "unknown"]
142
+ end
143
+
144
+ sig { params(error: StandardError).returns(String) }
145
+ def classify_error(error)
146
+ return "timeout" if error.is_a?(TimeoutError) || error.is_a?(Timeout::Error) || error.is_a?(Errno::ETIMEDOUT)
147
+ return "network_error" if error.is_a?(NetworkError) || error.is_a?(SocketError) || error.is_a?(SystemCallError)
148
+ return "http_#{error.status}" if error.is_a?(APIError)
149
+ return "decode_error" if error.is_a?(JSON::ParserError)
150
+
151
+ "unknown_error"
152
+ end
153
+ end
154
+
155
+ private_constant :Telemetry
156
+ end
@@ -2,5 +2,5 @@
2
2
  # typed: strict
3
3
 
4
4
  module Inttegro
5
- VERSION = "4.1.0"
5
+ VERSION = "4.2.1"
6
6
  end
@@ -0,0 +1,77 @@
1
+ # typed: strict
2
+
3
+ # Minimal signatures for the opentelemetry-api surface used by this SDK.
4
+ module OpenTelemetry
5
+ sig { returns(Trace::TracerProvider) }
6
+ def self.tracer_provider; end
7
+
8
+ sig { returns(Context::Propagation::TextMapPropagator) }
9
+ def self.propagation; end
10
+
11
+ class Context
12
+ module Propagation
13
+ class TextMapPropagator
14
+ sig { params(carrier: T::Hash[String, String]).void }
15
+ def inject(carrier); end
16
+ end
17
+ end
18
+ end
19
+
20
+ module Trace
21
+ sig do
22
+ type_parameters(:Result)
23
+ .params(
24
+ span: Span,
25
+ block: T.proc.params(span: Span, context: OpenTelemetry::Context).returns(T.type_parameter(:Result))
26
+ )
27
+ .returns(T.type_parameter(:Result))
28
+ end
29
+ def self.with_span(span, &block); end
30
+
31
+ class TracerProvider
32
+ sig do
33
+ params(
34
+ deprecated_name: T.nilable(String),
35
+ deprecated_version: T.nilable(String),
36
+ name: T.nilable(String),
37
+ version: T.nilable(String),
38
+ attributes: T.nilable(Object)
39
+ ).returns(Tracer)
40
+ end
41
+ def tracer(deprecated_name = nil, deprecated_version = nil, name: nil, version: nil, attributes: nil); end
42
+ end
43
+
44
+ class Tracer
45
+ sig do
46
+ params(
47
+ name: String,
48
+ with_parent: T.nilable(Object),
49
+ attributes: T.nilable(Object),
50
+ links: T.nilable(Object),
51
+ start_timestamp: T.nilable(Time),
52
+ kind: T.nilable(Symbol)
53
+ ).returns(Span)
54
+ end
55
+ def start_span(name, with_parent: nil, attributes: nil, links: nil, start_timestamp: nil, kind: nil); end
56
+ end
57
+
58
+ class Span
59
+ sig { params(key: String, value: Object).returns(Span) }
60
+ def set_attribute(key, value); end
61
+
62
+ sig { params(name: String, attributes: T.nilable(Object), timestamp: T.nilable(Time)).returns(Span) }
63
+ def add_event(name, attributes: nil, timestamp: nil); end
64
+
65
+ sig { params(status: Status).void }
66
+ def status=(status); end
67
+
68
+ sig { void }
69
+ def finish; end
70
+ end
71
+
72
+ class Status
73
+ sig { params(description: String).returns(Status) }
74
+ def self.error(description = ""); end
75
+ end
76
+ end
77
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inttegro
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.0
4
+ version: 4.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Inttegro Engineering
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-09-03 00:00:00.000000000 Z
11
+ date: 2026-09-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sorbet-runtime
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: opentelemetry-api
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
27
41
  description: First-party Ruby client for working with orders, payments, payouts, notifications,
28
42
  and verification on Inttegro.
29
43
  email:
@@ -73,10 +87,12 @@ files:
73
87
  - lib/inttegro/resources/spec.rb
74
88
  - lib/inttegro/resources/upload_requests.rb
75
89
  - lib/inttegro/response_object.rb
90
+ - lib/inttegro/telemetry.rb
76
91
  - lib/inttegro/transport_response.rb
77
92
  - lib/inttegro/types.rb
78
93
  - lib/inttegro/version.rb
79
94
  - rbi/inttegro/generated.rbi
95
+ - rbi/opentelemetry-api.rbi
80
96
  homepage: https://studio.inttegro.com
81
97
  licenses:
82
98
  - MIT