manza 1.2.0 → 1.3.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: 23d95fcba1fba943b2dd02913102b71a982ad3548745a5a249245ab2ff120211
4
- data.tar.gz: 38790ad40aa6bb4c3183f5114eee891e473bff53cbfcb1b2810b0ce986ba9b7e
3
+ metadata.gz: 460a7caba9195a45a73b84bbadbd1fdc3476ad4fdf8244e0d554fd451b493b28
4
+ data.tar.gz: ecabf7b8a26eeb01ff6405821305ad54e9192674f77982df77f3b53437aefeaa
5
5
  SHA512:
6
- metadata.gz: ee8d889d2ec4437866067a0806ae30d381f4efdb3a6548dd79fa9223dee1c831b890dfef7f5ee0c3068147af6acf4ad23ffaec78967b5f7776c05ceb7e51f299
7
- data.tar.gz: 28f2074be599fe6edced2ca521b636797cd066f49d40a0a9e5dc22b8705df6884757763fd51420ee8be13a36d33b54b83f67598ed0f4d6de940843328da873df
6
+ metadata.gz: b17cb8c504555b47d2c26322880a22c9187396617a1fcfdbda5a4d176e6e4671e5ca4164d528454ba69008ec11841658a42c262a9619219a07b9605dba0e23fc
7
+ data.tar.gz: c69b5d6f786843c19f815a8eb1b201653b833d90fe4128796f30b7eee81f46df59096df33d0b9c6d04e08e7f00d595b3d837c7bb3677d856ab3e2243ff45509d
data/CHANGELOG.md CHANGED
@@ -7,6 +7,47 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Added
11
+
12
+ - `manza.payments` (`Manza::Resources::Payments`): `create`, `get`,
13
+ `authorize` and `decline` on the API's canonical paths, `POST
14
+ /api/payments`, `GET /api/payments/:id`, `POST /api/payments/:id/authorize`
15
+ and `POST /api/payments/:id/decline`. The API renamed transfer drafts to
16
+ payments; request and response bodies are unchanged, and so is the
17
+ `Manza::TransferAuthorization` signature input.
18
+
19
+ ### Deprecated
20
+
21
+ - `manza.transfer_drafts` and `Manza::Resources::TransferDrafts`. Both keep
22
+ working for all of 1.x: the accessor returns `manza.payments` and the
23
+ constant names the same class, so the old name now calls `/api/payments`
24
+ too. They warn only when Ruby's deprecation warnings are on
25
+ (`Warning[:deprecated] = true` or `ruby -W:deprecated`). The server keeps
26
+ `/api/transfer_drafts` as a permanent alias.
27
+
28
+ ### Changed (cassettes)
29
+
30
+ The nine payment cassettes moved from `transfer_drafts/` to `payments/`
31
+ and match a new URI. An SDK that replays `cassettes-v1.3.0` sends:
32
+
33
+ | Cassette | Request |
34
+ |---|---|
35
+ | `payments/create` | `POST https://ma.manza.dev/api/payments` |
36
+ | `payments/create_duplicate` | `POST https://ma.manza.dev/api/payments` |
37
+ | `payments/get` | `GET https://ma.manza.dev/api/payments/fixture-transfer-draft-id` |
38
+ | `payments/get_refused` | `GET https://ma.manza.dev/api/payments/fixture-refused-draft-id` |
39
+ | `payments/authorize` | `POST https://ma.manza.dev/api/payments/fixture-authorizable-draft-id/authorize` |
40
+ | `payments/authorize_bad_signature` | `POST https://ma.manza.dev/api/payments/fixture-bad-signature-draft-id/authorize` |
41
+ | `payments/authorize_same_key` | `POST https://ma.manza.dev/api/payments/fixture-authorizable-draft-id/authorize` |
42
+ | `payments/authorize_new_payee` | `POST https://ma.manza.dev/api/payments/fixture-new-payee-draft-id/authorize` |
43
+ | `payments/decline` | `POST https://ma.manza.dev/api/payments/fixture-declinable-draft-id/decline` |
44
+
45
+ Each was `/api/transfer_drafts…` with the same rest of the path, under
46
+ `transfer_drafts/<name>`. The fixture placeholders keep their names
47
+ (`fixture-transfer-draft-id`, …). Every other cassette was re-recorded with
48
+ the same method, URI and request body as in `cassettes-v1.2.0`, and every
49
+ response keeps its status and the same JSON keys.
50
+
10
51
  ## [1.2.0]
11
52
 
12
53
  1.1.0 was never published: its changes (the webhook verifier and the
data/README.md CHANGED
@@ -88,14 +88,15 @@ manza.beneficiaries.create_external_account("01a0...", account_number: "007780..
88
88
  manza.payee_trust_requests.create(external_account_ids: ["01a1..."])
89
89
  manza.payee_trust_requests.get("01a2...")
90
90
 
91
- manza.transfer_drafts.create(
91
+ manza.payments.create(
92
92
  account_id: "019dde7d-...",
93
93
  beneficiary_id: "01a0...",
94
94
  amount: "2500.00",
95
95
  client_reference: "po_1042" # unique per entity; a duplicate raises Manza::ConflictError
96
96
  )
97
- manza.transfer_drafts.get("01a3...") # authorization: status, refusal_reason
98
- manza.transfer_drafts.decline("01a3...", authorization_id: "01a4...", reason: "Not ours")
97
+ manza.payments.get("01a3...") # authorization: status, refusal_reason
98
+ manza.payments.decline("01a3...", authorization_id: "01a4...", reason: "Not ours")
99
+ # manza.transfer_drafts is a deprecated alias of manza.payments (same methods, /api/payments)
99
100
 
100
101
  manza.transfer_authorizer.get # status, suspension_reason, limits, daily_authorized_total; NotFoundError: none enrolled
101
102
 
@@ -167,7 +168,7 @@ input = Manza::TransferAuthorization.signature_input(
167
168
  signature = Manza::TransferAuthorization.sign(secret: signing_secret, signature_input: input)
168
169
 
169
170
  authorizer = Manza.new(api_key: ENV["MANZA_AUTHORIZER_API_KEY"])
170
- authorizer.transfer_drafts.authorize(draft["id"], authorization_id: webhook["data"]["authorization"]["id"], signature: signature)
171
+ authorizer.payments.authorize(draft["id"], authorization_id: webhook["data"]["authorization"]["id"], signature: signature)
171
172
  ```
172
173
 
173
174
  A wrong signature raises `Manza::ValidationError` (`type` `invalid_signature`). Five on one challenge send the draft to your in-app approvers; five in a row suspend the authorizer.
data/lib/manza/client.rb CHANGED
@@ -87,6 +87,10 @@ module Manza
87
87
  @payment_links ||= Resources::PaymentLinks.new(self)
88
88
  end
89
89
 
90
+ def payments
91
+ @payments ||= Resources::Payments.new(self)
92
+ end
93
+
90
94
  def payee_trust_requests
91
95
  @payee_trust_requests ||= Resources::PayeeTrustRequests.new(self)
92
96
  end
@@ -95,8 +99,12 @@ module Manza
95
99
  @transfer_authorizer ||= Resources::TransferAuthorizer.new(self)
96
100
  end
97
101
 
102
+ # @deprecated Use {#payments}. Kept for all of 1.x: returns the same
103
+ # resource, which calls `/api/payments`. Warns when Ruby's deprecation
104
+ # warnings are on (`Warning[:deprecated] = true` or `-W:deprecated`).
98
105
  def transfer_drafts
99
- @transfer_drafts ||= Resources::TransferDrafts.new(self)
106
+ warn("[manza] transfer_drafts is deprecated; use payments instead.", category: :deprecated, uplevel: 1)
107
+ payments
100
108
  end
101
109
 
102
110
  def webhook_endpoints
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Manza
4
+ module Resources
5
+ # API-initiated transfers, called payments (formerly transfer drafts;
6
+ # `/api/transfer_drafts` stays a permanent server-side alias). Creating
7
+ # a draft never executes a transfer by itself. A draft inside the
8
+ # entity's machine-
9
+ # authorization envelope (trusted payee, within limits) is sent to
10
+ # the enrolled transfer authorizer as a `payment.authorization_requested`
11
+ # webhook; answer it with {#authorize} or {#decline}, using an API
12
+ # key other than the one that created the draft. Every other draft
13
+ # goes to the in-app approval flow, where a manager or legal
14
+ # representative approves it. Poll {#get} (status: requested →
15
+ # processing → completed / failed) or subscribe to the
16
+ # `transfer.executed` webhook to follow execution.
17
+ #
18
+ # A draft's `authorization` says which way it went: `pending` (a
19
+ # challenge was sent), `authorized`, `declined` (the draft is deleted),
20
+ # or one of two outcomes that send the draft to the in-app approvers,
21
+ # each with a `refusal_reason`:
22
+ #
23
+ # - `refused`: it did not qualify, at create time (no webhook is sent)
24
+ # or again when the challenge was answered: `untrusted_payee`,
25
+ # `unrestricted_api_key`, `over_new_payee_limit`,
26
+ # `over_transfer_limit`, `over_daily_limit`, `currency_mismatch`,
27
+ # `suspended` or `secret_expired`.
28
+ # - `expired` / `superseded` / `failed`: a challenge was sent and closed
29
+ # without an authorize: `expired` (no answer within the hour),
30
+ # `stale` (the draft, its payee's bank details or the endpoint changed
31
+ # while it was open) or `signature_failed` (five invalid signatures).
32
+ # One exception: the in-app approval that finalizes the draft while
33
+ # its challenge is open supersedes it (`stale`), and the draft
34
+ # executes or is scheduled; the draft's own `status` shows which.
35
+ #
36
+ # `authorization` is nil when no authorizer was enrolled.
37
+ #
38
+ # With "Approve transfers to new payees" on (enrolment, in the app),
39
+ # a draft to bank details the entity never trusted is challenged too,
40
+ # up to the new-payee limit, when the creating key has an IP
41
+ # allowlist; authorizing it also trusts the payee.
42
+ class Payments < Base
43
+ # POST /api/payments
44
+ #
45
+ # Required: account_id, amount, and exactly one of beneficiary_id
46
+ # (external transfer) or destination_account_id (own-account move).
47
+ # Optional: external_account_id, currency_code, payment_reference,
48
+ # internal_notes, client_reference (unique per entity, at most 128
49
+ # characters; a duplicate raises {Manza::ConflictError} whose
50
+ # `payment_id` names the existing draft).
51
+ def create(**attributes)
52
+ http_post("api/payments", body: attributes)
53
+ end
54
+
55
+ # GET /api/payments/:id
56
+ def get(id)
57
+ http_get(encode_path("api/payments", id))
58
+ end
59
+
60
+ # POST /api/payments/:id/authorize
61
+ #
62
+ # Executes the draft. `authorization_id` comes from the
63
+ # `payment.authorization_requested` webhook; build `signature` with
64
+ # {Manza::TransferAuthorization}. Requires the `transfers:authorize`
65
+ # scope on a key other than the draft's creator (otherwise 403
66
+ # `same_key_forbidden`), bound to an IP allowlist (otherwise 403
67
+ # `unrestricted_api_key`). A 409 {Manza::ConflictError} with `type`
68
+ # `authorization_busy` means a bank-detail edit, trust or trust
69
+ # revocation on the payee was in flight: retry. A blank signature
70
+ # raises {Manza::ArgumentError} and is never sent: the API would count
71
+ # it as a failed attempt. Five invalid signatures fail the challenge.
72
+ def authorize(id, authorization_id:, signature:)
73
+ raise Manza::ArgumentError, "signature cannot be blank" if signature.to_s.strip.empty?
74
+
75
+ http_post(
76
+ encode_path("api/payments", id, "authorize"),
77
+ body: { authorization_id: authorization_id, signature: signature }
78
+ )
79
+ end
80
+
81
+ # POST /api/payments/:id/decline
82
+ #
83
+ # Declines the challenge and deletes the draft. Returns the
84
+ # authorization (`status: "declined"`, `declined_at`, and a nil
85
+ # `refusal_reason`).
86
+ def decline(id, authorization_id:, reason: nil)
87
+ http_post(
88
+ encode_path("api/payments", id, "decline"),
89
+ body: { authorization_id: authorization_id, reason: reason }.compact
90
+ )
91
+ end
92
+ end
93
+ end
94
+ end
@@ -2,91 +2,9 @@
2
2
 
3
3
  module Manza
4
4
  module Resources
5
- # API-initiated transfers. Creating a draft never executes a
6
- # transfer by itself. A draft inside the entity's machine-
7
- # authorization envelope (trusted payee, within limits) is sent to
8
- # the enrolled transfer authorizer as a `payment.authorization_requested`
9
- # webhook; answer it with {#authorize} or {#decline}, using an API
10
- # key other than the one that created the draft. Every other draft
11
- # goes to the in-app approval flow, where a manager or legal
12
- # representative approves it. Poll {#get} (status: requested →
13
- # processing → completed / failed) or subscribe to the
14
- # `transfer.executed` webhook to follow execution.
15
- #
16
- # A draft's `authorization` says which way it went: `pending` (a
17
- # challenge was sent), `authorized`, `declined` (the draft is deleted),
18
- # or one of two outcomes that send the draft to the in-app approvers,
19
- # each with a `refusal_reason`:
20
- #
21
- # - `refused`: it did not qualify, at create time (no webhook is sent)
22
- # or again when the challenge was answered: `untrusted_payee`,
23
- # `unrestricted_api_key`, `over_new_payee_limit`,
24
- # `over_transfer_limit`, `over_daily_limit`, `currency_mismatch`,
25
- # `suspended` or `secret_expired`.
26
- # - `expired` / `superseded` / `failed`: a challenge was sent and closed
27
- # without an authorize: `expired` (no answer within the hour),
28
- # `stale` (the draft, its payee's bank details or the endpoint changed
29
- # while it was open) or `signature_failed` (five invalid signatures).
30
- # One exception: the in-app approval that finalizes the draft while
31
- # its challenge is open supersedes it (`stale`), and the draft
32
- # executes or is scheduled; the draft's own `status` shows which.
33
- #
34
- # `authorization` is nil when no authorizer was enrolled.
35
- #
36
- # With "Approve transfers to new payees" on (enrolment, in the app),
37
- # a draft to bank details the entity never trusted is challenged too,
38
- # up to the new-payee limit, when the creating key has an IP
39
- # allowlist; authorizing it also trusts the payee.
40
- class TransferDrafts < Base
41
- # POST /api/transfer_drafts
42
- #
43
- # Required: account_id, amount, and exactly one of beneficiary_id
44
- # (external transfer) or destination_account_id (own-account move).
45
- # Optional: external_account_id, currency_code, payment_reference,
46
- # internal_notes, client_reference (unique per entity, at most 128
47
- # characters; a duplicate raises {Manza::ConflictError} whose
48
- # `payment_id` names the existing draft).
49
- def create(**attributes)
50
- http_post("api/transfer_drafts", body: attributes)
51
- end
52
-
53
- # GET /api/transfer_drafts/:id
54
- def get(id)
55
- http_get(encode_path("api/transfer_drafts", id))
56
- end
57
-
58
- # POST /api/transfer_drafts/:id/authorize
59
- #
60
- # Executes the draft. `authorization_id` comes from the
61
- # `payment.authorization_requested` webhook; build `signature` with
62
- # {Manza::TransferAuthorization}. Requires the `transfers:authorize`
63
- # scope on a key other than the draft's creator (otherwise 403
64
- # `same_key_forbidden`), bound to an IP allowlist (otherwise 403
65
- # `unrestricted_api_key`). A 409 {Manza::ConflictError} with `type`
66
- # `authorization_busy` means a bank-detail edit, trust or trust
67
- # revocation on the payee was in flight: retry. A blank signature
68
- # raises {Manza::ArgumentError} and is never sent: the API would count
69
- # it as a failed attempt. Five invalid signatures fail the challenge.
70
- def authorize(id, authorization_id:, signature:)
71
- raise Manza::ArgumentError, "signature cannot be blank" if signature.to_s.strip.empty?
72
-
73
- http_post(
74
- encode_path("api/transfer_drafts", id, "authorize"),
75
- body: { authorization_id: authorization_id, signature: signature }
76
- )
77
- end
78
-
79
- # POST /api/transfer_drafts/:id/decline
80
- #
81
- # Declines the challenge and deletes the draft. Returns the
82
- # authorization (`status: "declined"`, `declined_at`, and a nil
83
- # `refusal_reason`).
84
- def decline(id, authorization_id:, reason: nil)
85
- http_post(
86
- encode_path("api/transfer_drafts", id, "decline"),
87
- body: { authorization_id: authorization_id, reason: reason }.compact
88
- )
89
- end
90
- end
5
+ # @deprecated Use {Payments} (`manza.payments`). Kept for all of 1.x;
6
+ # it is the same class and calls `/api/payments`.
7
+ TransferDrafts = Payments
8
+ deprecate_constant :TransferDrafts
91
9
  end
92
10
  end
@@ -11,7 +11,7 @@ module Manza
11
11
  # from your *own* record of the transfer (not the webhook's
12
12
  # `signature_input`, which is there only to compare against), sign
13
13
  # it with the authorizer endpoint's signing secret, and pass the
14
- # result to {Resources::TransferDrafts#authorize}:
14
+ # result to {Resources::Payments#authorize}:
15
15
  #
16
16
  # input = Manza::TransferAuthorization.signature_input(
17
17
  # payment_id: draft["id"], nonce: nonce, amount: draft["amount"],
@@ -20,7 +20,7 @@ module Manza
20
20
  # client_reference: draft["client_reference"]
21
21
  # )
22
22
  # signature = Manza::TransferAuthorization.sign(secret: signing_secret, signature_input: input)
23
- # manza.transfer_drafts.authorize(draft["id"], authorization_id: authorization_id, signature: signature)
23
+ # manza.payments.authorize(draft["id"], authorization_id: authorization_id, signature: signature)
24
24
  module TransferAuthorization
25
25
  SIGNATURE_VERSION = "manza.transfer-authorization.v1"
26
26
 
data/lib/manza/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Manza
4
- VERSION = "1.2.0"
4
+ VERSION = "1.3.0"
5
5
  end
data/lib/manza.rb CHANGED
@@ -31,6 +31,7 @@ require_relative "manza/resources/entity"
31
31
  require_relative "manza/resources/invoices"
32
32
  require_relative "manza/resources/payee_trust_requests"
33
33
  require_relative "manza/resources/payment_links"
34
+ require_relative "manza/resources/payments"
34
35
  require_relative "manza/resources/transfer_authorizer"
35
36
  require_relative "manza/resources/transfer_drafts"
36
37
  require_relative "manza/resources/webhook_endpoints"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: manza
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Manza
@@ -76,6 +76,7 @@ files:
76
76
  - lib/manza/resources/invoices.rb
77
77
  - lib/manza/resources/payee_trust_requests.rb
78
78
  - lib/manza/resources/payment_links.rb
79
+ - lib/manza/resources/payments.rb
79
80
  - lib/manza/resources/transfer_authorizer.rb
80
81
  - lib/manza/resources/transfer_drafts.rb
81
82
  - lib/manza/resources/webhook_endpoints.rb