paystack_sdk 0.3.0 → 0.4.0

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: 8893378c4721f9f126d73b888dd539060c93058488b49c92833183b819d06023
4
- data.tar.gz: a787c8b3b413e4171c3574e38dc519c0b2eed3df09776b383a0b20fc259bd75a
3
+ metadata.gz: da9c2a9982f9eb4ec639e53651b7a54a20e0adda1d0f3f21b2add3bcf51194b2
4
+ data.tar.gz: 12bc63e0ebc8e8448f2a73cbabe2230af8c0f783857b3931dc72973dfcb3581a
5
5
  SHA512:
6
- metadata.gz: a205e604c23fc3ca07a1ad7096ef920c9587fc04d322da9c40e6e00bd92870c2d833f554b275c6651a3f1aa07c23d56e2cb77fc41d2a02005e5848f8686942a4
7
- data.tar.gz: d5cbb5b6bfefe710024e1cba4dcaa2097577b4df3f17e001199b281945cb8d417000c89a0fd5292d8ca8444272d55313f010b2acfadc130246372433f0465166
6
+ metadata.gz: ebf5ddb53f888f1d9aaa017b4aa9af1542f28aa563654f13de77ffc78f83043073c7aa2134e030853695e06dcc98ee88e5688d1e682462c4036df239e8605f26
7
+ data.tar.gz: 2f7b2710265dcab7de4367ab68da1e7112e963d6a165ed3024f7881b81dc44032b0c706afddf896d52a4c0e5999208c397ae55c78ad07c5c5fefc2af13bc865d
data/CHANGELOG.md CHANGED
@@ -2,6 +2,24 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.4.0] - 2026-10-09
6
+
7
+ ### Added
8
+
9
+ - The SDK now covers every operation in Paystack's OpenAPI spec (163 of 163). Resources that could not be exercised against the Paystack test API (Terminals, Dedicated Virtual Accounts, and the state-changing calls on Apple Pay, Integrations, Virtual Terminals and Direct Debits) follow the spec and docs, and their README sections and method docs say so.
10
+ - `Pages` (`client.pages`), generated from Paystack's OpenAPI spec: `create`, `list`, `fetch(id_or_slug:)`, `update(id_or_slug:)`, `check_slug_availability(slug:)` and `add_products(id:, products:)`. Checked against the Paystack test API, `fetch` and `update` take the numeric ID or the slug, `update` needs no `name` or `description` (the docs mark them required), and `add_products` reads `products` (the docs say `product`) and takes the numeric ID only.
11
+ - `Products` (`client.products`), generated from Paystack's OpenAPI spec: `create`, `list`, `fetch(id:)`, `update(id:, ...)` and `delete(id:)`. `delete` (`DELETE /product/{id}`) is in the spec but on no docs page; the test API deletes the product. Checked against the test API, `create` does not require `description` (the spec and docs say it does; `name`, `price` and `currency` are required) and takes `metadata` as a Hash (the spec says a JSON string, which Paystack stores as an object of its characters). Products are addressed by numeric ID.
12
+ - `Orders` (`client.orders`): `create`, `list`, `fetch(id:)`, `fetch_product_orders(id:)` (the product's ID) and `validate(code:)` (a GET that takes the order code), generated from Paystack's OpenAPI spec. `create` follows the spec (`email`, `first_name`, `last_name`, `phone`, `currency`, `items`, `shipping`): the test API rejects the shape on Paystack's docs page (`customer`, `line_items`) with "Customer email is required". `items` are `{item: <product ID>, type: "product", quantity:, amount:}` and `shipping` needs `street_line`, `city`, `state`, `country` and `shipping_fee`. `fetch(id:)` returned a generic API error for a just-created order in the test API.
13
+ - `Storefronts` (`client.storefronts`), generated from Paystack's OpenAPI spec: `create`, `list`, `fetch(id:)`, `update(id:)`, `delete(id:)`, `verify_slug(slug:)`, `fetch_orders(id:)`, `add_products(id:, products:)`, `list_products(id:)`, `publish(id:)` and `duplicate(id:)`. Checked against the Paystack test API, `create` requires `slug` and `currency` (the docs mark slug optional and omit currency), `update` takes `slug`, `add_products` takes numeric product IDs, and `publish` copies the storefront and its products to the live integration even with a test key.
14
+ - `bin/scaffold_names.yml` entries can carry a `note:`, rendered as a `@note` in the method's docs. `storefronts.publish` uses it to warn that publishing creates a live copy even with a test key.
15
+ - `BulkCharges` (`client.bulk_charges`), generated from Paystack's OpenAPI spec: `initiate(charges:)`, `list_batches`, `fetch_batch(id_or_code:)`, `fetch_charges(id_or_code:)`, `pause_batch(batch_code:)` and `resume_batch(batch_code:)`. `initiate` sends `charges` (an Array of Hashes with `authorization`, `amount` and `reference`) as the JSON array body. Pause and resume are GET requests, as the docs and the test API have them. `from` and `to`, which the docs list, are left out: the test API ignores them.
16
+ - `bin/paystack-scaffold` takes a `body:` keyword in `bin/scaffold_names.yml` for an operation whose body is a JSON array (default `items`), and names that keyword, not an empty string, in the missing-parameter error.
17
+ - `ApplePay` (`client.apple_pay`), generated from Paystack's OpenAPI spec: `list_domains`, `register_domain(domain_name:)` and `unregister_domain(domain_name:)`. Only `list_domains` was checked against the Paystack test API (it answers without `use_cursor`, which the docs mark required). Registering and unregistering change account-wide Apple Pay configuration that may reach live mode with a test key, so they were not called and are unverified; their method docs carry a `@note` saying so. `bin/paystack-scaffold` can name a singular resource (`SINGULAR_TAGS`), used so the tag "Apple Pay" gives `ApplePay`, not `ApplePays`.
18
+ - `Integrations` (`client.integrations`), generated from Paystack's OpenAPI spec: `fetch_payment_session_timeout` and `update_payment_session_timeout(timeout:)` (seconds). Only the fetch was checked against the Paystack test API. The update changes an account-wide setting that may be shared with live mode, so it was not called and is unverified; its docs carry a warning.
19
+ - `VirtualTerminals` (`client.virtual_terminals`), generated from Paystack's OpenAPI spec: `create`, `list`, `fetch(code:)`, `update(code:, name:)`, `deactivate(code:)`, `assign_destination(code:, destinations:)`, `unassign_destination(code:, targets:)`, `add_split_code(code:, split_code:)` and `remove_split_code(code:, split_code:)` (a DELETE with a JSON body). `list` also takes `status` (`active` or `inactive`), which the docs list and the test API validates but the spec omits. Checked against the Paystack test API: `list`, `fetch`, validation errors and unknown-code errors only. Nothing was created, and assign and unassign destination were not called (a destination is a WhatsApp number), so those, `create` with a valid body and the split code operations on a real terminal are unverified.
20
+ - `DirectDebits` (`client.direct_debits`), generated from Paystack's OpenAPI spec: `list_mandate_authorizations(cursor:, status:, per_page:)` and `trigger_activation_charge(customer_ids:)`. These are the integration-wide `/directdebit/...` endpoints; the per-customer ones stay on `client.customers`. `list_mandate_authorizations` was checked against the test API (all parameters optional, `status` limited to pending, active and revoked). `trigger_activation_charge` charges customers' bank accounts and follows the docs and spec without a live check.
21
+ - `Terminals` (`client.terminals`), generated from Paystack's OpenAPI spec: `send_event(terminal_id:)`, `fetch_event_status(terminal_id:, event_id:)`, `fetch_status(terminal_id:)`, `list`, `fetch(terminal_id:)`, `update(terminal_id:)`, `commission(serial_number:)` and `decommission(serial_number:)`. Unverified against the Paystack API: the test account has no Terminal access (every GET returns 403 "not yet available in your country"), so the methods follow the spec, and the device operations were never called.
22
+
5
23
  ## [0.3.0] - 2026-10-09
6
24
 
7
25
  ### Added
data/README.md CHANGED
@@ -53,6 +53,7 @@ The `paystack_sdk` gem provides a simple and intuitive interface for interacting
53
53
  - [Create a Subaccount](#create-a-subaccount)
54
54
  - [List and Fetch Subaccounts](#list-and-fetch-subaccounts)
55
55
  - [Update a Subaccount](#update-a-subaccount)
56
+ - [Orders](#orders)
56
57
  - [Balances](#balances)
57
58
  - [Plans](#plans)
58
59
  - [Create a Plan](#create-a-plan)
@@ -67,10 +68,38 @@ The `paystack_sdk` gem provides a simple and intuitive interface for interacting
67
68
  - [Create or Assign an Account](#create-or-assign-an-account)
68
69
  - [List, Fetch, Requery and Deactivate](#list-fetch-requery-and-deactivate)
69
70
  - [Split Payments on an Account](#split-payments-on-an-account)
71
+ - [Pages](#pages)
72
+ - [List, Fetch and Update Pages](#list-fetch-and-update-pages)
70
73
  - [Payment Requests](#payment-requests)
71
74
  - [Create a Payment Request](#create-a-payment-request)
72
75
  - [List, Fetch and Verify Payment Requests](#list-fetch-and-verify-payment-requests)
73
76
  - [Update, Finalize, Notify and Archive](#update-finalize-notify-and-archive)
77
+ - [Products](#products)
78
+ - [Create a Product](#create-a-product)
79
+ - [List, Fetch, Update and Delete Products](#list-fetch-update-and-delete-products)
80
+ - [Storefronts](#storefronts)
81
+ - [Create a Storefront](#create-a-storefront)
82
+ - [List, Fetch, Update and Delete Storefronts](#list-fetch-update-and-delete-storefronts)
83
+ - [Products, Orders, Duplicate and Publish](#products-orders-duplicate-and-publish)
84
+ - [Bulk Charges](#bulk-charges)
85
+ - [Initiate a Bulk Charge](#initiate-a-bulk-charge)
86
+ - [List and Fetch Batches and Their Charges](#list-and-fetch-batches-and-their-charges)
87
+ - [Pause and Resume a Batch](#pause-and-resume-a-batch)
88
+ - [Apple Pay](#apple-pay)
89
+ - [List Registered Domains](#list-registered-domains)
90
+ - [Register and Unregister a Domain (unverified)](#register-and-unregister-a-domain-unverified)
91
+ - [Integration](#integration)
92
+ - [Payment Session Timeout](#payment-session-timeout)
93
+ - [Virtual Terminals](#virtual-terminals)
94
+ - [Create, List, Fetch, Update and Deactivate](#create-list-fetch-update-and-deactivate-virtual-terminals)
95
+ - [Destinations and Split Codes](#destinations-and-split-codes)
96
+ - [Direct Debit](#direct-debit)
97
+ - [List Mandate Authorizations](#list-mandate-authorizations)
98
+ - [Trigger Activation Charges](#trigger-activation-charges)
99
+ - [Terminals](#terminals)
100
+ - [List and Fetch Terminals](#list-and-fetch-terminals)
101
+ - [Send an Event and Check It](#send-an-event-and-check-it)
102
+ - [Update, Commission and Decommission](#update-commission-and-decommission)
74
103
  - [Response Handling](#response-handling)
75
104
  - [Working with Response Objects](#working-with-response-objects)
76
105
  - [Accessing the Original Response](#accessing-the-original-response)
@@ -698,10 +727,11 @@ paystack.customers.initialize_direct_debit(
698
727
  address: {street: "Some Where", city: "Ikeja", state: "Lagos"}
699
728
  )
700
729
 
701
- # The customer's Direct Debit mandates
730
+ # The customer's Direct Debit mandates (every customer's: paystack.direct_debits.list_mandate_authorizations)
702
731
  paystack.customers.fetch_mandate_authorizations(id: 12345)
703
732
 
704
- # Trigger an activation charge on an inactive mandate
733
+ # Trigger an activation charge on an inactive mandate (pending mandates of several customers:
734
+ # paystack.direct_debits.trigger_activation_charge, see Direct Debit below)
705
735
  paystack.customers.direct_debit_activation_charge(id: 12345, authorization_id: 1069309917)
706
736
 
707
737
  # Deactivate an authorization (any channel)
@@ -1098,6 +1128,29 @@ paystack.subaccounts.update(id_or_code: "ACCT_6uujpqtzmnufzkw", active: false)
1098
1128
  paystack.subaccounts.update(id_or_code: "ACCT_6uujpqtzmnufzkw", bank_code: "040100", account_number: "1234567890123")
1099
1129
  ```
1100
1130
 
1131
+ ### Orders
1132
+
1133
+ Orders record a customer's purchase of your products. `create` places an order for a customer, so it may notify the buyer: use test mode and a test email while you build.
1134
+
1135
+ ```ruby
1136
+ response = paystack.orders.create(
1137
+ email: "ama@example.com",
1138
+ first_name: "Ama",
1139
+ last_name: "Mensah",
1140
+ phone: "+233200000000",
1141
+ currency: "GHS",
1142
+ items: [{item: 2782725, type: "product", quantity: 2, amount: 20_000}], # product ID; amount in pesewas
1143
+ shipping: {street_line: "1 Road", city: "Accra", state: "Greater Accra", country: "Ghana", shipping_fee: 0}
1144
+ )
1145
+
1146
+ order = paystack.orders.fetch(id: 12_345) # numeric order ID
1147
+ paystack.orders.list(per_page: 20, from: "2026-01-01") # filters: per_page, page, from, to
1148
+ paystack.orders.fetch_product_orders(id: 2782725) # orders for one product (the product ID)
1149
+ paystack.orders.validate(code: "ORD_abc123def456") # GET, for a "pay for me" order
1150
+ ```
1151
+
1152
+ Paystack's docs page shows `create` taking `customer` and `line_items`, but the API follows the OpenAPI spec: the test API answers the docs' shape with "Customer email is required". The order total must be at least 2 units of the currency (GHS 2), and a new order has status `created` until it is paid. `pay_for_me: true` needs receiver details that neither the spec nor the docs describe ("Receiver data is required"), so it is not covered here.
1153
+
1101
1154
  ### Balances
1102
1155
 
1103
1156
  Read-only. Amounts are integers in the currency's subunit (pesewas for GHS, kobo for NGN), so `48400` is GHS 484.00. Paystack documents both operations on its Transfers Control page.
@@ -1364,6 +1417,352 @@ paystack.payment_requests.notify(code: "PRQ_7w7dpecncnebg7e")
1364
1417
  paystack.payment_requests.archive(id_or_code: "PRQ_7w7dpecncnebg7e")
1365
1418
  ```
1366
1419
 
1420
+ ### Pages
1421
+
1422
+ A payment page is a public pay link at `https://paystack.com/pay/<slug>`, useful for donations or one-off payments. Amounts are integers in the currency's subunit (pesewas for GHS).
1423
+
1424
+ ```ruby
1425
+ # Without an amount the customer chooses what to pay (a donation page). Pass fixed_amount: true
1426
+ # together with amount to fix it.
1427
+ response = paystack.pages.create(
1428
+ name: "Sunday Offering",
1429
+ description: "Give to the work of the church",
1430
+ slug: "sunday-offering",
1431
+ collect_phone: true,
1432
+ custom_fields: [{display_name: "Branch", variable_name: "branch"}]
1433
+ )
1434
+ response.data.slug # => "sunday-offering"
1435
+
1436
+ # type is payment (default), subscription (with plan:), product or plan
1437
+ paystack.pages.create(name: "Lite plan", type: "subscription", plan: "4288882")
1438
+
1439
+ # Is a slug free? An unsuccessful response ("Slug already in use") means it is taken.
1440
+ paystack.pages.check_slug_availability(slug: "sunday-offering").success?
1441
+ ```
1442
+
1443
+ #### List, Fetch and Update Pages
1444
+
1445
+ ```ruby
1446
+ paystack.pages.list(per_page: 20, page: 1)
1447
+
1448
+ # Fetch and update take the numeric ID or the slug
1449
+ paystack.pages.fetch(id_or_slug: "sunday-offering")
1450
+ paystack.pages.update(id_or_slug: "sunday-offering", description: "New description")
1451
+
1452
+ # Deactivate the page URL
1453
+ paystack.pages.update(id_or_slug: 2215275, active: false)
1454
+
1455
+ # Product pages only: add products by numeric ID (the page needs type: "product")
1456
+ paystack.pages.add_products(id: 2215275, products: [473, 292])
1457
+ ```
1458
+
1459
+ ### Products
1460
+
1461
+ A product is something you sell through Paystack (a good, with stock if you track it). Prices are integers in the subunit of the currency: pesewas for GHS, kobo for NGN, cents for ZAR or USD. Only the currencies enabled on your integration are accepted.
1462
+
1463
+ #### Create a Product
1464
+
1465
+ ```ruby
1466
+ response = paystack.products.create(
1467
+ name: "Church anniversary t-shirt",
1468
+ price: 5000, # GHS 50.00, in pesewas
1469
+ currency: "GHS",
1470
+ description: "Cotton, sizes S to XL",
1471
+ quantity: 100, # stock on hand; use unlimited: true instead if you do not track stock
1472
+ metadata: {branch: "Osu"} # a Hash; Paystack stores a JSON string as an object of its characters
1473
+ )
1474
+
1475
+ if response.success?
1476
+ puts "Created #{response.data.product_code} (ID #{response.data.id})"
1477
+ else
1478
+ puts "Error: #{response.error_message}"
1479
+ end
1480
+ ```
1481
+
1482
+ `name`, `price` and `currency` are required by Paystack. Its docs and spec also list `description` as required, but the test API creates a product without one.
1483
+
1484
+ #### List, Fetch, Update and Delete Products
1485
+
1486
+ ```ruby
1487
+ paystack.products.list(per_page: 20, page: 1, active: true)
1488
+
1489
+ # Products are fetched, updated and deleted by their numeric ID (not the PROD_ code)
1490
+ paystack.products.fetch(id: 2782723)
1491
+
1492
+ # Send only what changes
1493
+ paystack.products.update(id: 2782723, price: 6000, quantity: 80)
1494
+
1495
+ paystack.products.delete(id: 2782723)
1496
+ ```
1497
+
1498
+ `delete` calls `DELETE /product/{id}`, which is in Paystack's OpenAPI spec but on no docs page; the test API deletes the product and answers `404 Product not found` afterwards.
1499
+ ### Storefronts
1500
+
1501
+ A storefront is a Paystack-hosted shop (`https://paystack.shop/<slug>`) that sells products you created with the Products API. Storefronts are identified by their numeric ID.
1502
+
1503
+ #### Create a Storefront
1504
+
1505
+ ```ruby
1506
+ # Paystack requires name, slug and currency (its docs mark slug optional; the API does not).
1507
+ # The slug must be 5 to 100 characters: lowercase letters, numbers, dashes or underscores.
1508
+ response = paystack.storefronts.create(
1509
+ name: "Harvest stall",
1510
+ slug: "harvest-stall",
1511
+ currency: "GHS", # one your integration accepts; Paystack refuses others ("Currency not supported or allowed")
1512
+ description: "Sunday harvest sale"
1513
+ )
1514
+
1515
+ puts response.data.id if response.success?
1516
+ ```
1517
+
1518
+ Check a slug first with `verify_slug`. Paystack answers with the storefront that holds it when it is taken (in test or live mode), and with a 404 "Storefront not found" when it is free:
1519
+
1520
+ ```ruby
1521
+ paystack.storefronts.verify_slug(slug: "harvest-stall").success? # => false: the slug is free
1522
+ ```
1523
+
1524
+ #### List, Fetch, Update and Delete Storefronts
1525
+
1526
+ ```ruby
1527
+ paystack.storefronts.list(status: "active", per_page: 20) # status is active or inactive
1528
+ paystack.storefronts.fetch(id: 1852308)
1529
+
1530
+ # Send only what changes; update returns a message, not the storefront
1531
+ paystack.storefronts.update(id: 1852308, slug: "harvest-stall-2", description: "Harvest weekend")
1532
+
1533
+ # Paystack keeps a deleted storefront with status "deleted"; it drops out of list
1534
+ paystack.storefronts.delete(id: 1852308)
1535
+ ```
1536
+
1537
+ #### Products, Orders, Duplicate and Publish
1538
+
1539
+ ```ruby
1540
+ # products takes numeric product IDs (Paystack refuses PROD_ codes here)
1541
+ paystack.storefronts.add_products(id: 1852308, products: [2782728])
1542
+ paystack.storefronts.list_products(id: 1852308)
1543
+ paystack.storefronts.fetch_orders(id: 1852308)
1544
+
1545
+ # A copy with its products, named "Copy of ..." and given a new slug
1546
+ paystack.storefronts.duplicate(id: 1852308)
1547
+
1548
+ # Publish copies the storefront and its products to your live integration, even with a test key
1549
+ paystack.storefronts.publish(id: 1852308)
1550
+ ```
1551
+
1552
+ `publish` makes a public, live storefront. On the test API it answered "Storefront published to live" and returned a new storefront with `domain: "live"` and a new ID, gave it the original slug, and renamed the test storefront (adding "-TEST" to the name and giving it a new slug). The live copy cannot be fetched or deleted with a test key: remove it from the dashboard in live mode.
1553
+
1554
+ ### Bulk Charges
1555
+
1556
+ A bulk charge charges many saved cards (reusable authorizations) in one batch, which Paystack queues and processes in the background. Amounts are in the subunit (pesewas, kobo, cents).
1557
+
1558
+ #### Initiate a Bulk Charge
1559
+
1560
+ ```ruby
1561
+ # The body is a JSON array: one Hash per charge, each with a reusable authorization code and an
1562
+ # integer amount. Give each a reference you can find again: it becomes the charge's transaction
1563
+ # reference (lowercase letters, digits, - and _, per the spec).
1564
+ response = paystack.bulk_charges.initiate(
1565
+ charges: [
1566
+ {authorization: "AUTH_50w0d0f5xo", amount: 5000, reference: "tithe-2026-10-kwesi"},
1567
+ {authorization: "AUTH_xfuz7dy4b9", amount: 2000, reference: "tithe-2026-10-ama"}
1568
+ ]
1569
+ )
1570
+
1571
+ if response.success?
1572
+ puts "Queued #{response.data.batch_code} with #{response.data.total_charges} charges"
1573
+ else
1574
+ puts "Error: #{response.error_message}"
1575
+ end
1576
+ ```
1577
+
1578
+ The SDK refuses an empty list but sends each Hash as given: Paystack, not the SDK, checks what is inside. A successful response means the charges were queued ("Charges have been queued"), not that they succeeded; read each charge's outcome with `fetch_charges` or from your webhooks. Like every write, `initiate` is not retried after a timeout, so check `list_batches` before sending the same charges again.
1579
+
1580
+ #### List and Fetch Batches and Their Charges
1581
+
1582
+ ```ruby
1583
+ # status is active, paused or complete. Paystack's docs also list from and to, but the API ignores them.
1584
+ paystack.bulk_charges.list_batches(status: "active", per_page: 20, page: 1)
1585
+
1586
+ # Fetch by numeric ID or BCH_ code; total_charges and pending_charges show its progress
1587
+ batch = paystack.bulk_charges.fetch_batch(id_or_code: "BCH_1uhxe0d181eu850")
1588
+ puts "#{batch.data.pending_charges} of #{batch.data.total_charges} still pending"
1589
+
1590
+ # Each charge with its customer, authorization, reference and status
1591
+ # (pending, success, failed, error or inactive_authorization)
1592
+ paystack.bulk_charges.fetch_charges(id_or_code: "BCH_1uhxe0d181eu850", status: "failed")
1593
+ ```
1594
+
1595
+ #### Pause and Resume a Batch
1596
+
1597
+ ```ruby
1598
+ # Both are GET requests, as Paystack documents them, and take the BCH_ batch code
1599
+ paystack.bulk_charges.pause_batch(batch_code: "BCH_1uhxe0d181eu850") # "Bulk charge batch has been paused"
1600
+ paystack.bulk_charges.resume_batch(batch_code: "BCH_1uhxe0d181eu850") # "Bulk charge batch has been resumed"
1601
+ ```
1602
+
1603
+ A paused batch keeps its pending charges until it is resumed.
1604
+
1605
+ ### Apple Pay
1606
+
1607
+ Apple Pay on Paystack needs each domain (or subdomain) that shows the Apple Pay button to be registered on your integration. The accessor is `paystack.apple_pay`.
1608
+
1609
+ #### List Registered Domains
1610
+
1611
+ ```ruby
1612
+ response = paystack.apple_pay.list_domains
1613
+
1614
+ # An account with no domains answers "Apple Pay registered domains retrieved" with an empty list
1615
+ puts response.data.domainNames.inspect # => []
1616
+ ```
1617
+
1618
+ `list_domains` was checked against the Paystack test API. Paystack's docs mark `use_cursor` required, but the API answers without it (and with `use_cursor=true`), so the SDK keeps it optional. Cursor pagination (`next_cursor:`, sent as `next`, and `previous:`) could not be exercised, because the test account has no domains.
1619
+
1620
+ #### Register and Unregister a Domain (unverified)
1621
+
1622
+ ```ruby
1623
+ paystack.apple_pay.register_domain(domain_name: "pay.example.com")
1624
+ paystack.apple_pay.unregister_domain(domain_name: "pay.example.com")
1625
+ ```
1626
+
1627
+ **These two methods have not been called against the Paystack API.** They change the account's Apple Pay domain configuration, which is account-wide and public and may reach live mode even with a test key (`sk_test_`), so they were deliberately not tried. They follow Paystack's docs and OpenAPI spec: one domain per call, sent as `domainName`, and `unregister_domain` is a `DELETE` that carries the domain in a JSON body. How Paystack answers (and whether it reads a body on `DELETE`) is unconfirmed. Try them with a domain you own, and check the result in the dashboard.
1628
+
1629
+ ### Integration
1630
+
1631
+ Settings of your Paystack integration (the account). Currently the payment session timeout.
1632
+
1633
+ #### Payment Session Timeout
1634
+
1635
+ ```ruby
1636
+ # Seconds before a transaction becomes invalid. The test API returned 0 for an account that never set one.
1637
+ response = paystack.integrations.fetch_payment_session_timeout
1638
+ puts response.data.payment_session_timeout
1639
+
1640
+ paystack.integrations.update_payment_session_timeout(timeout: 30)
1641
+ ```
1642
+
1643
+ `update_payment_session_timeout` changes a setting of the whole integration, which may be shared with live mode even when you use a test key, so treat it as a live change. It is **unverified**: it was not called against the Paystack API while building the SDK for that reason. It is generated from the OpenAPI spec and the docs page (`PUT /integration/payment_session_timeout`, body `timeout`, an integer in seconds). Only `fetch_payment_session_timeout` was checked against the API.
1644
+
1645
+ ### Virtual Terminals
1646
+
1647
+ A virtual terminal lets you accept in-person payments without a POS device: payments made to it notify its destinations, which are WhatsApp numbers. Check that the product is enabled for your business.
1648
+
1649
+ #### Create, List, Fetch, Update and Deactivate Virtual Terminals
1650
+
1651
+ ```ruby
1652
+ response = paystack.virtual_terminals.create(
1653
+ name: "Front desk",
1654
+ destinations: [{target: "+2341234567890", name: "Front desk phone"}]
1655
+ )
1656
+ puts response.data.code # "VT_..."
1657
+
1658
+ # status is active or inactive (the docs list it, the OpenAPI spec omits it)
1659
+ paystack.virtual_terminals.list(status: "active", per_page: 20)
1660
+
1661
+ paystack.virtual_terminals.fetch(code: "VT_MCK5292Z")
1662
+ paystack.virtual_terminals.update(code: "VT_MCK5292Z", name: "New terminal name")
1663
+ paystack.virtual_terminals.deactivate(code: "VT_MCK5292Z") # "Terminal set to inactive"
1664
+ ```
1665
+
1666
+ On the Paystack test API, `list` and `fetch` work (an account with no terminals gets an empty list). `create` answers 400 `"destinations" is required` without destinations, `"destinations" must contain at least 1 items` with an empty list and `invalid phone number ...` for a target that is not a phone number; `fetch` of an unknown code answers 404 `Virtual Terminal not found`. `create` with a valid body was not run (it needs a real phone number), so the optional `split_code` and `metadata` and the docs' `currency` and `custom_fields` are unverified; the SDK sends what the spec lists.
1667
+
1668
+ #### Destinations and Split Codes
1669
+
1670
+ ```ruby
1671
+ paystack.virtual_terminals.assign_destination(
1672
+ code: "VT_MCK5292Z",
1673
+ destinations: [{target: "+2341234567890", name: "Another one"}]
1674
+ )
1675
+ paystack.virtual_terminals.unassign_destination(code: "VT_MCK5292Z", targets: ["+2341234567890"])
1676
+
1677
+ paystack.virtual_terminals.add_split_code(code: "VT_MCK5292Z", split_code: "SPL_98WF13Zu8w5")
1678
+ # DELETE with a JSON body, as Paystack documents it
1679
+ paystack.virtual_terminals.remove_split_code(code: "VT_MCK5292Z", split_code: "SPL_98WF13Zu8w5")
1680
+ ```
1681
+
1682
+ Assign and unassign destination change who receives payment notifications on WhatsApp, so they were not called against the test API and are described from Paystack's docs only (unverified). The split code operations were only run against an unknown terminal: an unknown split code answers 400 `Invalid split code`, and removing one that is not assigned answers 400 `Virtual Terminal split code assignment does not exist`.
1683
+
1684
+ ### Direct Debit
1685
+
1686
+ `client.direct_debits` works across your whole integration: every customer's Direct Debit mandates at once, and activation charges for several customers in one call. To link a bank account, or to work with one customer's mandates by their customer ID, use the methods on `client.customers` (see [Authorizations and Direct Debit](#authorizations-and-direct-debit)):
1687
+
1688
+ | You want to | Integration-wide (`direct_debits`) | One customer (`customers`) |
1689
+ |---|---|---|
1690
+ | See mandates | `list_mandate_authorizations` (`GET /directdebit/mandate-authorizations`), filtered by `status`, paged by cursor | `fetch_mandate_authorizations(id:)` (`GET /customer/{id}/directdebit-mandate-authorizations`) |
1691
+ | Trigger an activation charge | `trigger_activation_charge(customer_ids:)` (`PUT /directdebit/activation-charge`), for **pending** mandates | `direct_debit_activation_charge(id:, authorization_id:)` (`PUT /customer/{id}/directdebit-activation-charge`), for one **inactive** mandate |
1692
+
1693
+ #### List Mandate Authorizations
1694
+
1695
+ ```ruby
1696
+ # All parameters are optional. status is pending, active or revoked
1697
+ response = paystack.direct_debits.list_mandate_authorizations(status: "active", per_page: 10)
1698
+
1699
+ response.each do |mandate|
1700
+ puts "#{mandate.authorization_code} #{mandate.bank_name} #{mandate.status}"
1701
+ end
1702
+
1703
+ # Cursor pagination: pass meta.next back as cursor (it is null on the last page)
1704
+ next_cursor = response.meta.next
1705
+ paystack.direct_debits.list_mandate_authorizations(status: "active", per_page: 10, cursor: next_cursor) if next_cursor
1706
+ ```
1707
+
1708
+ Paystack answers a cursor it cannot read with a 500, which the SDK raises as `PaystackSdk::ServerError`.
1709
+
1710
+ #### Trigger Activation Charges
1711
+
1712
+ ```ruby
1713
+ # Numeric customer IDs of customers with pending mandates
1714
+ response = paystack.direct_debits.trigger_activation_charge(customer_ids: [28958104, 983697220])
1715
+ response.message # => "Mandate is queued for retry"
1716
+ ```
1717
+
1718
+ This charges the customers' bank accounts. It has not been verified against the test API: the method follows Paystack's docs and OpenAPI spec.
1719
+
1720
+ ### Terminals
1721
+
1722
+ A Paystack Terminal is a physical point-of-sale device linked to your integration. **None of this was verified against the Paystack API:** the test account used to build the SDK (Ghana) gets `403` "Sorry, this feature is not yet available in your country." from every Terminal GET (list, fetch, status, event status; the others were not called), which the SDK returns as an unsuccessful `Response`. The methods follow Paystack's OpenAPI spec and docs page.
1723
+
1724
+ #### List and Fetch Terminals
1725
+
1726
+ ```ruby
1727
+ # Cursor pagination: pass the meta's next or previous cursor back. per_page is sent as the spec names
1728
+ # it (the docs say perPage; which one the API reads is unverified).
1729
+ response = paystack.terminals.list(per_page: 20)
1730
+ paystack.terminals.list(next_cursor: response.meta.next) if response.success? && response.meta.next
1731
+
1732
+ terminal = paystack.terminals.fetch(terminal_id: "2872S934")
1733
+
1734
+ # Check a terminal is online and available before sending it an event
1735
+ status = paystack.terminals.fetch_status(terminal_id: "2872S934")
1736
+ puts "online: #{status.data.online}, available: #{status.data.available}"
1737
+ ```
1738
+
1739
+ #### Send an Event and Check It
1740
+
1741
+ ```ruby
1742
+ # Pushes an event to the physical device. type is invoice or transaction; action is process or view
1743
+ # (invoice) or process or print (transaction).
1744
+ event = paystack.terminals.send_event(
1745
+ terminal_id: "2872S934",
1746
+ type: "invoice",
1747
+ action: "process",
1748
+ data: {id: 7895939, reference: 4634337895939}
1749
+ )
1750
+
1751
+ paystack.terminals.fetch_event_status(terminal_id: "2872S934", event_id: event.data.id) # data.delivered
1752
+ ```
1753
+
1754
+ The spec marks `type`, `action` and `data` optional and the SDK follows it; the docs list them without an "optional" badge.
1755
+
1756
+ #### Update, Commission and Decommission
1757
+
1758
+ ```ruby
1759
+ paystack.terminals.update(terminal_id: "2872S934", name: "Front desk", address: "Accra")
1760
+
1761
+ # Link or unlink a debug device by its serial number
1762
+ paystack.terminals.commission(serial_number: "1111150412230003899")
1763
+ paystack.terminals.decommission(serial_number: "1111150412230003899")
1764
+ ```
1765
+
1367
1766
  ### Response Handling
1368
1767
 
1369
1768
  #### Working with Response Objects
@@ -17,6 +17,16 @@ require_relative "resources/plans"
17
17
  require_relative "resources/subscriptions"
18
18
  require_relative "resources/dedicated_virtual_accounts"
19
19
  require_relative "resources/payment_requests"
20
+ require_relative "resources/pages"
21
+ require_relative "resources/products"
22
+ require_relative "resources/orders"
23
+ require_relative "resources/storefronts"
24
+ require_relative "resources/bulk_charges"
25
+ require_relative "resources/apple_pay"
26
+ require_relative "resources/integrations"
27
+ require_relative "resources/virtual_terminals"
28
+ require_relative "resources/direct_debits"
29
+ require_relative "resources/terminals"
20
30
  require_relative "utils/connection_utils"
21
31
 
22
32
  module PaystackSdk
@@ -254,6 +264,86 @@ module PaystackSdk
254
264
  @payment_requests ||= Resources::PaymentRequests.new(@connection)
255
265
  end
256
266
 
267
+ # Provides access to the `Pages` resource.
268
+ #
269
+ # @return [PaystackSdk::Resources::Pages] An instance of the
270
+ # `Pages` resource.
271
+ def pages
272
+ @pages ||= Resources::Pages.new(@connection)
273
+ end
274
+
275
+ # Provides access to the `Products` resource.
276
+ #
277
+ # @return [PaystackSdk::Resources::Products] An instance of the
278
+ # `Products` resource.
279
+ def products
280
+ @products ||= Resources::Products.new(@connection)
281
+ end
282
+
283
+ # Provides access to the `Orders` resource.
284
+ #
285
+ # @return [PaystackSdk::Resources::Orders] An instance of the
286
+ # `Orders` resource.
287
+ def orders
288
+ @orders ||= Resources::Orders.new(@connection)
289
+ end
290
+
291
+ # Provides access to the `Storefronts` resource.
292
+ #
293
+ # @return [PaystackSdk::Resources::Storefronts] An instance of the
294
+ # `Storefronts` resource.
295
+ def storefronts
296
+ @storefronts ||= Resources::Storefronts.new(@connection)
297
+ end
298
+
299
+ # Provides access to the `BulkCharges` resource.
300
+ #
301
+ # @return [PaystackSdk::Resources::BulkCharges] An instance of the
302
+ # `BulkCharges` resource.
303
+ def bulk_charges
304
+ @bulk_charges ||= Resources::BulkCharges.new(@connection)
305
+ end
306
+
307
+ # Provides access to the `ApplePay` resource.
308
+ #
309
+ # @return [PaystackSdk::Resources::ApplePay] An instance of the
310
+ # `ApplePay` resource.
311
+ def apple_pay
312
+ @apple_pay ||= Resources::ApplePay.new(@connection)
313
+ end
314
+
315
+ # Provides access to the `Integrations` resource.
316
+ #
317
+ # @return [PaystackSdk::Resources::Integrations] An instance of the
318
+ # `Integrations` resource.
319
+ def integrations
320
+ @integrations ||= Resources::Integrations.new(@connection)
321
+ end
322
+
323
+ # Provides access to the `VirtualTerminals` resource.
324
+ #
325
+ # @return [PaystackSdk::Resources::VirtualTerminals] An instance of the
326
+ # `VirtualTerminals` resource.
327
+ def virtual_terminals
328
+ @virtual_terminals ||= Resources::VirtualTerminals.new(@connection)
329
+ end
330
+
331
+ # Provides access to the `DirectDebits` resource.
332
+ #
333
+ # @return [PaystackSdk::Resources::DirectDebits] An instance of the
334
+ # `DirectDebits` resource.
335
+ def direct_debits
336
+ @direct_debits ||= Resources::DirectDebits.new(@connection)
337
+ end
338
+
339
+ # Provides access to the `Terminals` resource.
340
+ #
341
+ # @return [PaystackSdk::Resources::Terminals] An instance of the
342
+ # `Terminals` resource.
343
+ def terminals
344
+ @terminals ||= Resources::Terminals.new(@connection)
345
+ end
346
+
257
347
  private
258
348
 
259
349
  # The secret key a connection sends, read from its Authorization header.
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Generated by bin/paystack-scaffold from Paystack's OpenAPI spec (PaystackOSS/openapi@d9d444d).
4
+ # Do not edit by hand: regenerate it with `bin/paystack-scaffold Apple Pay`. Hand-written extras go in lib/paystack_sdk/resources/extensions/apple_pay.rb.
5
+ # scaffold-digest: cd91bbd0850ed1baf4fd266d161cc41c75551ecff1190aa614ba064fddd10c7a
6
+
7
+ require_relative "base"
8
+
9
+ module PaystackSdk
10
+ module Resources
11
+ # Apple Pay operations.
12
+ class ApplePay < PaystackSdk::Resources::Base
13
+ # Ruby keyword => the parameter name Paystack documents.
14
+ WIRE_NAMES = {next_cursor: "next", domain_name: "domainName"}.freeze
15
+
16
+ # List Domains.
17
+ #
18
+ # Lists all registered domains on your integration.
19
+ #
20
+ # @param use_cursor [Boolean] A flag to indicate if cursor based pagination should be used
21
+ # @param next_cursor [String] An alphanumeric value returned for every cursor based retrieval, used to retrieve the next set of data
22
+ # @param previous [String] An alphanumeric value returned for every cursor based retrieval, used to retrieve the previous set of data
23
+ # @return [PaystackSdk::Response] The response from the Paystack API.
24
+ # @raise [PaystackSdk::Error] If a parameter is invalid or the API request fails.
25
+ # @see https://paystack.com/docs/api/apple-pay/#list-domains
26
+ def list_domains(use_cursor: nil, next_cursor: nil, previous: nil)
27
+ wire_query = to_wire({use_cursor:, next_cursor:, previous:}, WIRE_NAMES)
28
+
29
+ handle_response(@connection.get("/apple-pay/domain", wire_query))
30
+ end
31
+
32
+ # Register Domain.
33
+ #
34
+ # Register a top-level domain or subdomain for your Apple Pay integration.
35
+ #
36
+ # @param domain_name [String] The domain or subdomain for your application
37
+ # @note Registers a domain for Apple Pay on the whole account. This changes Apple Pay
38
+ # configuration that may also affect live mode, even with a test key (sk_test_). Not
39
+ # verified against the live API by this SDK: do not call it to try the API out.
40
+ # @return [PaystackSdk::Response] The response from the Paystack API.
41
+ # @raise [PaystackSdk::Error] If a parameter is invalid or the API request fails.
42
+ # @see https://paystack.com/docs/api/apple-pay/#register-domain
43
+ def register_domain(domain_name:)
44
+ validate_presence!(value: domain_name, name: "domainName")
45
+
46
+ wire_body = to_wire({domain_name:}, WIRE_NAMES)
47
+
48
+ handle_response(@connection.post("/apple-pay/domain", wire_body))
49
+ end
50
+
51
+ # Unregister Domain.
52
+ #
53
+ # Unregister a top-level domain or subdomain previously used for your Apple Pay integration.
54
+ #
55
+ # @param domain_name [String] The domain or subdomain for your application
56
+ # @note Unregisters a domain from Apple Pay on the whole account. This changes Apple Pay
57
+ # configuration that may also affect live mode, even with a test key (sk_test_). Not
58
+ # verified against the live API by this SDK, including how Paystack reads the request body
59
+ # of a DELETE.
60
+ # @return [PaystackSdk::Response] The response from the Paystack API.
61
+ # @raise [PaystackSdk::Error] If a parameter is invalid or the API request fails.
62
+ # @see https://paystack.com/docs/api/apple-pay/#unregister-domain
63
+ def unregister_domain(domain_name:)
64
+ validate_presence!(value: domain_name, name: "domainName")
65
+
66
+ wire_body = to_wire({domain_name:}, WIRE_NAMES)
67
+
68
+ handle_response(
69
+ @connection.delete("/apple-pay/domain") { |request| request.body = wire_body }
70
+ )
71
+ end
72
+ end
73
+ end
74
+ end