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 +4 -4
- data/CHANGELOG.md +41 -0
- data/README.md +5 -4
- data/lib/manza/client.rb +9 -1
- data/lib/manza/resources/payments.rb +94 -0
- data/lib/manza/resources/transfer_drafts.rb +4 -86
- data/lib/manza/transfer_authorization.rb +2 -2
- data/lib/manza/version.rb +1 -1
- data/lib/manza.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 460a7caba9195a45a73b84bbadbd1fdc3476ad4fdf8244e0d554fd451b493b28
|
|
4
|
+
data.tar.gz: ecabf7b8a26eeb01ff6405821305ad54e9192674f77982df77f3b53437aefeaa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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.
|
|
98
|
-
manza.
|
|
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.
|
|
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
|
-
|
|
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
|
-
#
|
|
6
|
-
#
|
|
7
|
-
|
|
8
|
-
|
|
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::
|
|
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.
|
|
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
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.
|
|
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
|