Skip to content

Commit e754fbb

Browse files
pcarranzavclaude
andcommitted
feat: add receipt-based payment system to replace TAP receipts
- Add GetReceiptById RPC method for polling payment status - Modify CollectPaymentResponse to return receipt_id, amount, and status - Add dips_receipts table migration for testing compatibility - Regenerate proto bindings with new fields - Bump @graphprotocol/dips-proto version to 0.3.0 This implements the indexer-service changes for the new DIPs Safe payment system as specified in the architecture plan. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent ec8f5bb commit e754fbb

File tree

5 files changed

+197
-4
lines changed

5 files changed

+197
-4
lines changed

crates/dips/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@graphprotocol/dips-proto",
3-
"version": "0.2.2",
3+
"version": "0.3.0",
44
"main": "generated/index.js",
55
"types": "generated/index.d.ts",
66
"files": [

crates/dips/proto/gateway.proto

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ service GatewayDipsService {
1818
* and receive payment for the indexing work done.
1919
*/
2020
rpc CollectPayment(CollectPaymentRequest) returns (CollectPaymentResponse);
21+
22+
/**
23+
* Get the status of a payment receipt by ID.
24+
*
25+
* This method allows the indexer to poll for the status of a previously
26+
* initiated payment collection.
27+
*/
28+
rpc GetReceiptById(GetReceiptByIdRequest) returns (GetReceiptByIdResponse);
2129
}
2230

2331

@@ -58,7 +66,9 @@ message CollectPaymentRequest {
5866
message CollectPaymentResponse {
5967
uint64 version = 1;
6068
CollectPaymentStatus status = 2;
61-
bytes tap_receipt = 3;
69+
string receipt_id = 3; // Receipt ID for polling
70+
string amount = 4; // Payment amount in GRT
71+
string payment_status = 5; // Initial status: "PENDING"
6272
}
6373

6474
/**
@@ -71,3 +81,28 @@ enum CollectPaymentStatus {
7181
ERR_AMOUNT_OUT_OF_BOUNDS = 3; /// The payment request is for too large an amount
7282
ERR_UNKNOWN = 99; /// Something else went terribly wrong
7383
}
84+
85+
/**
86+
* A request to get receipt status by ID.
87+
*
88+
* See the `GatewayDipsService.GetReceiptById` method.
89+
*/
90+
message GetReceiptByIdRequest {
91+
uint64 version = 1;
92+
string receipt_id = 2;
93+
}
94+
95+
/**
96+
* A response containing the receipt status.
97+
*
98+
* See the `GatewayDipsService.GetReceiptById` method.
99+
*/
100+
message GetReceiptByIdResponse {
101+
uint64 version = 1;
102+
string receipt_id = 2;
103+
string status = 3; // "PENDING" | "SUBMITTED" | "FAILED"
104+
string transaction_hash = 4; // Present when SUBMITTED
105+
string error_message = 5; // Present when FAILED
106+
string amount = 6;
107+
string payment_submitted_at = 7; // ISO timestamp when SUBMITTED
108+
}

crates/dips/src/proto/graphprotocol.gateway.dips.rs

Lines changed: 140 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,51 @@ pub struct CollectPaymentResponse {
4040
pub version: u64,
4141
#[prost(enumeration = "CollectPaymentStatus", tag = "2")]
4242
pub status: i32,
43-
#[prost(bytes = "vec", tag = "3")]
44-
pub tap_receipt: ::prost::alloc::vec::Vec<u8>,
43+
/// Receipt ID for polling
44+
#[prost(string, tag = "3")]
45+
pub receipt_id: ::prost::alloc::string::String,
46+
/// Payment amount in GRT
47+
#[prost(string, tag = "4")]
48+
pub amount: ::prost::alloc::string::String,
49+
/// Initial status: "PENDING"
50+
#[prost(string, tag = "5")]
51+
pub payment_status: ::prost::alloc::string::String,
52+
}
53+
/// *
54+
/// A request to get receipt status by ID.
55+
///
56+
/// See the `GatewayDipsService.GetReceiptById` method.
57+
#[derive(Clone, PartialEq, ::prost::Message)]
58+
pub struct GetReceiptByIdRequest {
59+
#[prost(uint64, tag = "1")]
60+
pub version: u64,
61+
#[prost(string, tag = "2")]
62+
pub receipt_id: ::prost::alloc::string::String,
63+
}
64+
/// *
65+
/// A response containing the receipt status.
66+
///
67+
/// See the `GatewayDipsService.GetReceiptById` method.
68+
#[derive(Clone, PartialEq, ::prost::Message)]
69+
pub struct GetReceiptByIdResponse {
70+
#[prost(uint64, tag = "1")]
71+
pub version: u64,
72+
#[prost(string, tag = "2")]
73+
pub receipt_id: ::prost::alloc::string::String,
74+
/// "PENDING" | "SUBMITTED" | "FAILED"
75+
#[prost(string, tag = "3")]
76+
pub status: ::prost::alloc::string::String,
77+
/// Present when SUBMITTED
78+
#[prost(string, tag = "4")]
79+
pub transaction_hash: ::prost::alloc::string::String,
80+
/// Present when FAILED
81+
#[prost(string, tag = "5")]
82+
pub error_message: ::prost::alloc::string::String,
83+
#[prost(string, tag = "6")]
84+
pub amount: ::prost::alloc::string::String,
85+
/// ISO timestamp when SUBMITTED
86+
#[prost(string, tag = "7")]
87+
pub payment_submitted_at: ::prost::alloc::string::String,
4588
}
4689
/// *
4790
/// The status on response to collect an _indexing agreement_.
@@ -244,6 +287,40 @@ pub mod gateway_dips_service_client {
244287
);
245288
self.inner.unary(req, path, codec).await
246289
}
290+
/// *
291+
/// Get the status of a payment receipt by ID.
292+
///
293+
/// This method allows the indexer to poll for the status of a previously
294+
/// initiated payment collection.
295+
pub async fn get_receipt_by_id(
296+
&mut self,
297+
request: impl tonic::IntoRequest<super::GetReceiptByIdRequest>,
298+
) -> std::result::Result<
299+
tonic::Response<super::GetReceiptByIdResponse>,
300+
tonic::Status,
301+
> {
302+
self.inner
303+
.ready()
304+
.await
305+
.map_err(|e| {
306+
tonic::Status::unknown(
307+
format!("Service was not ready: {}", e.into()),
308+
)
309+
})?;
310+
let codec = tonic::codec::ProstCodec::default();
311+
let path = http::uri::PathAndQuery::from_static(
312+
"/graphprotocol.gateway.dips.GatewayDipsService/GetReceiptById",
313+
);
314+
let mut req = request.into_request();
315+
req.extensions_mut()
316+
.insert(
317+
GrpcMethod::new(
318+
"graphprotocol.gateway.dips.GatewayDipsService",
319+
"GetReceiptById",
320+
),
321+
);
322+
self.inner.unary(req, path, codec).await
323+
}
247324
}
248325
}
249326
/// Generated server implementations.
@@ -283,6 +360,18 @@ pub mod gateway_dips_service_server {
283360
tonic::Response<super::CollectPaymentResponse>,
284361
tonic::Status,
285362
>;
363+
/// *
364+
/// Get the status of a payment receipt by ID.
365+
///
366+
/// This method allows the indexer to poll for the status of a previously
367+
/// initiated payment collection.
368+
async fn get_receipt_by_id(
369+
&self,
370+
request: tonic::Request<super::GetReceiptByIdRequest>,
371+
) -> std::result::Result<
372+
tonic::Response<super::GetReceiptByIdResponse>,
373+
tonic::Status,
374+
>;
286375
}
287376
#[derive(Debug)]
288377
pub struct GatewayDipsServiceServer<T> {
@@ -452,6 +541,55 @@ pub mod gateway_dips_service_server {
452541
};
453542
Box::pin(fut)
454543
}
544+
"/graphprotocol.gateway.dips.GatewayDipsService/GetReceiptById" => {
545+
#[allow(non_camel_case_types)]
546+
struct GetReceiptByIdSvc<T: GatewayDipsService>(pub Arc<T>);
547+
impl<
548+
T: GatewayDipsService,
549+
> tonic::server::UnaryService<super::GetReceiptByIdRequest>
550+
for GetReceiptByIdSvc<T> {
551+
type Response = super::GetReceiptByIdResponse;
552+
type Future = BoxFuture<
553+
tonic::Response<Self::Response>,
554+
tonic::Status,
555+
>;
556+
fn call(
557+
&mut self,
558+
request: tonic::Request<super::GetReceiptByIdRequest>,
559+
) -> Self::Future {
560+
let inner = Arc::clone(&self.0);
561+
let fut = async move {
562+
<T as GatewayDipsService>::get_receipt_by_id(
563+
&inner,
564+
request,
565+
)
566+
.await
567+
};
568+
Box::pin(fut)
569+
}
570+
}
571+
let accept_compression_encodings = self.accept_compression_encodings;
572+
let send_compression_encodings = self.send_compression_encodings;
573+
let max_decoding_message_size = self.max_decoding_message_size;
574+
let max_encoding_message_size = self.max_encoding_message_size;
575+
let inner = self.inner.clone();
576+
let fut = async move {
577+
let method = GetReceiptByIdSvc(inner);
578+
let codec = tonic::codec::ProstCodec::default();
579+
let mut grpc = tonic::server::Grpc::new(codec)
580+
.apply_compression_config(
581+
accept_compression_encodings,
582+
send_compression_encodings,
583+
)
584+
.apply_max_message_size_config(
585+
max_decoding_message_size,
586+
max_encoding_message_size,
587+
);
588+
let res = grpc.unary(method, req).await;
589+
Ok(res)
590+
};
591+
Box::pin(fut)
592+
}
455593
_ => {
456594
Box::pin(async move {
457595
let mut response = http::Response::new(
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-- Add down migration script here
2+
3+
DROP TABLE IF EXISTS dips_receipts;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
-- Add up migration script here
2+
3+
CREATE TABLE IF NOT EXISTS dips_receipts (
4+
id VARCHAR(255) PRIMARY KEY,
5+
agreement_id UUID NOT NULL REFERENCES indexing_agreements(id),
6+
amount NUMERIC(39) NOT NULL,
7+
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
8+
transaction_hash CHAR(66),
9+
error_message TEXT,
10+
created_at TIMESTAMP WITH TIME ZONE NOT NULL,
11+
updated_at TIMESTAMP WITH TIME ZONE NOT NULL,
12+
retry_count INTEGER NOT NULL DEFAULT 0,
13+
CONSTRAINT valid_status CHECK (status IN ('PENDING', 'SUBMITTED', 'FAILED'))
14+
);
15+
16+
CREATE INDEX idx_dips_receipts_agreement_id ON dips_receipts(agreement_id);
17+
CREATE INDEX idx_dips_receipts_status ON dips_receipts(status);

0 commit comments

Comments
 (0)