Skip to content

Commit

Permalink
Merge pull request #146 from golemfactory/kek/payment-status
Browse files Browse the repository at this point in the history
Add payment status endpoint
  • Loading branch information
kamirr authored Oct 9, 2023
2 parents afcd7eb + 5ac470c commit 2bb679e
Show file tree
Hide file tree
Showing 9 changed files with 228 additions and 33 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:

env:
CARGO_TERM_COLOR: always
rust_stable: 1.68.2
rust_stable: 1.71.1

jobs:
build:
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ ya-client-model = { version = "^0.5", path = "model" }
awc = { version = "3", default-features = false }
actix-codec = "0.5"
bytes = "1"
chrono = { version = "0.4", default-features = false }
chrono = { version = "0.4.31", default-features = false }
envy = "0.4"
futures = "0.3"
hex = "0.4"
Expand Down
2 changes: 1 addition & 1 deletion model/src/payment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub use self::invoice_event::{InvoiceEvent, InvoiceEventType};
pub use self::market_decoration::MarketDecoration;
pub use self::market_decoration::MarketProperty;
pub use self::network::Network;
pub use self::payment::Payment;
pub use self::payment::{DriverStatusProperty, Payment};
pub use self::rejection::Rejection;
pub use self::rejection_reason::RejectionReason;

Expand Down
7 changes: 3 additions & 4 deletions model/src/payment/debit_note_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,14 @@ mod test {
use super::*;
use crate::payment::{Rejection, RejectionReason};
use bigdecimal::{BigDecimal, FromPrimitive};
use chrono::TimeZone;

#[test]
fn test_serialize_rejected_event_has_flat_rejection() {
let ie = DebitNoteEvent {
debit_note_id: "ajdik".to_string(),
event_date: Utc
.datetime_from_str("2020-12-21T15:51:21.126645Z", "%+")
.unwrap(),
event_date: DateTime::parse_from_str("2020-12-21T15:51:21.126645Z", "%+")
.unwrap()
.into(),
event_type: DebitNoteEventType::DebitNoteRejectedEvent {
rejection: Rejection {
rejection_reason: RejectionReason::UnsolicitedService,
Expand Down
13 changes: 6 additions & 7 deletions model/src/payment/invoice_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,14 @@ mod test {
use super::*;
use crate::payment::{Rejection, RejectionReason};
use bigdecimal::{BigDecimal, FromPrimitive};
use chrono::TimeZone;

#[test]
fn test_serialize_rejected_event_has_flat_rejection() {
let ie = InvoiceEvent {
invoice_id: "ajdik".to_string(),
event_date: Utc
.datetime_from_str("2020-12-21T15:51:21.126645Z", "%+")
.unwrap(),
event_date: DateTime::parse_from_str("2020-12-21T15:51:21.126645Z", "%+")
.unwrap()
.into(),
event_type: InvoiceEventType::InvoiceRejectedEvent {
rejection: Rejection {
rejection_reason: RejectionReason::UnsolicitedService,
Expand Down Expand Up @@ -78,9 +77,9 @@ mod test {
assert_eq!(
InvoiceEvent {
invoice_id: "ajdik".to_string(),
event_date: Utc
.datetime_from_str("2020-12-21T15:51:21.126645Z", "%+")
.unwrap(),
event_date: DateTime::parse_from_str("2020-12-21T15:51:21.126645Z", "%+")
.unwrap()
.into(),
event_type: InvoiceEventType::InvoiceAcceptedEvent,
},
ie
Expand Down
8 changes: 8 additions & 0 deletions model/src/payment/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ pub struct DriverNetworkParams {
pub driver: Option<String>,
}

#[derive(Deserialize, Serialize)]
pub struct DriverStatusParams {
#[serde(default)]
pub network: Option<String>,
#[serde(default)]
pub driver: Option<String>,
}

#[derive(Deserialize, Serialize)]
pub struct AllocationIds {
#[serde(
Expand Down
77 changes: 77 additions & 0 deletions model/src/payment/payment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,80 @@ pub struct Payment {
pub activity_payments: Vec<ActivityPayment>,
pub details: String,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(tag = "kind")]
pub enum DriverStatusProperty {
InsufficientGas {
driver: String,
network: String,
#[serde(rename = "neededGasEst")]
needed_gas_est: String,
},
InsufficientToken {
driver: String,
network: String,
#[serde(rename = "neededTokenEst")]
needed_token_est: String,
},
InvalidChainId {
driver: String,
#[serde(rename = "chainId")]
chain_id: i64,
},
CantSign {
driver: String,
network: String,
address: String,
},
TxStuck {
driver: String,
network: String,
},
RpcError {
driver: String,
network: String,
},
}

#[cfg(test)]
mod tests {
use super::*;
use serde_json::{from_str, json, value::to_value};

#[test]
fn status_prop_serialization() {
assert_eq!(
json!({
"driver": "erc20",
"kind": "InsufficientGas",
"network": "foo",
"neededGasEst": "bar",
}),
to_value(&DriverStatusProperty::InsufficientGas {
driver: "erc20".into(),
network: "foo".into(),
needed_gas_est: "bar".into()
})
.unwrap()
);
}

#[test]
fn status_prop_deserialization() {
assert_eq!(
DriverStatusProperty::TxStuck {
driver: "erc20".into(),
network: "baz".into(),
},
from_str(
r#"{
"driver": "erc20",
"kind": "TxStuck",
"network": "baz"
}"#
)
.unwrap()
);
}
}
139 changes: 120 additions & 19 deletions specs/payment-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -360,25 +360,6 @@ paths:
500:
$ref: 'common.yaml#/responses/ServerError'

/payments/{paymentId}:
get:
tags:
- requestor
- provider
summary: Get Payment.
operationId: getPayment
parameters:
- $ref: '#/components/parameters/paymentId'
responses:
200:
$ref: '#/components/responses/Payment'
401:
$ref: 'common.yaml#/responses/Unauthorized'
404:
$ref: 'common.yaml#/responses/NotFound'
500:
$ref: 'common.yaml#/responses/ServerError'

/providerAccounts:
get:
tags:
Expand Down Expand Up @@ -683,6 +664,51 @@ paths:
500:
$ref: 'common.yaml#/responses/ServerError'

/payments/{paymentId}:
get:
tags:
- requestor
- provider
summary: Get Payment.
operationId: getPayment
parameters:
- $ref: '#/components/parameters/paymentId'
responses:
200:
$ref: '#/components/responses/Payment'
401:
$ref: 'common.yaml#/responses/Unauthorized'
404:
$ref: 'common.yaml#/responses/NotFound'
500:
$ref: 'common.yaml#/responses/ServerError'

/payments/status:
get:
tags:
- requestor
- provider
summary: Get status of the payment driver
description: >
This only relates to the erc20 driver, not erc20legacy. The returned
list contains individual status properties, which can be used to identify
problems like missing funds or misconfigured max fee per gas on a per-chain
(network) basis.
parameters:
- $ref: 'common.yaml#/parameters/network'
- $ref: 'common.yaml#/parameters/driver'
responses:
200:
$ref: '#/components/responses/DriverStatus'
400:
$ref: 'common.yaml#/responses/BadRequest'
401:
$ref: 'common.yaml#/responses/Unauthorized'
404:
$ref: 'common.yaml#/responses/NotFound'
500:
$ref: 'common.yaml#/responses/ServerError'

/requestorAccounts:
get:
tags:
Expand Down Expand Up @@ -893,6 +919,15 @@ components:
items:
$ref: '#/components/schemas/Payment'

DriverStatus:
description: OK
content:
application/json:
schema:
type: array
items:
$ref: '#components/schemas/DriverStatusProperty'

DebitNoteEventList:
description: OK
content:
Expand Down Expand Up @@ -1243,6 +1278,72 @@ components:
- activityPayments
- details

DriverStatusProperty:
type: object
description: Individual actionable property of the payment driver status
readOnly: true
properties:
kind:
description: >
Determines what property this is.
- InsufficientGas -- Driver cannot proceed due to missing gas.
- InsufficientToken -- Driver cannot proceed due to missing tokens.
- InvalidChainId -- A transaction can't be processed because its chain-id
isn't present in the configuration. This hints at a network being removed
from driver configuration after a transaction on this network has been
scheduled.
- CantSign -- Driver cannot sign transactions (locked wallet?).
- RpcError -- All configured RPC endpoints are failing.
- TxStuck -- Transaction was sent to blockchain successfully but cannot
proceed any further. Likely indicative of too low setting of max fee
per gas.
type: string
enum:
- InsufficientGas
- InsufficientToken
- InvalidChainId
- CantSign
- TxStuck
- RpcError
driver:
description: Payment driver to which this status property is applicable
type: string
network:
description: >
Indicates which chain the problem occurs on. No statuses other than
CantSign necessarily imply issues on other chains than the one the status
property originates from.
Present for all status properties other than InvalidChainId.
type: string
neededGasEst:
description: >
Estimate total required gas to complete all outstanding transactions.
Only present for InsufficientGas
type: string
neededTokenEst:
description: >
Estimate total required token to complete all outstanding transactions.
Only present for InsufficientToken
type: string
address:
description: >
Relates the status event to a specific blockchain address.
Only present for CantSign.
type: string
chainId:
description: >
Chain-id that the error relates to.
Only present for InvalidChainId.
type: integer
required:
- kind
- driver

AgreementPayment:
description: >
Share of a Payment assigned to an Agreement, but not to any particular
Expand Down
11 changes: 11 additions & 0 deletions src/payment/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,17 @@ impl PaymentApi {
let url = url_format!("payments/{payment_id}");
self.client.get(&url).send().json().await
}

pub async fn driver_status(
&self,
driver: Option<String>,
network: Option<String>,
) -> Result<Vec<DriverStatusProperty>> {
let params = params::DriverStatusParams { driver, network };
let url = url_format_obj("payments/status", &params);

self.client.get(&url).send().json().await
}
}

pub trait PaymentEvent: DeserializeOwned {
Expand Down

0 comments on commit 2bb679e

Please sign in to comment.