Create an outgoing payment grant request
The Grant Request API lets you request a grant for outgoing payment, incoming payment, and quote resources.
Before your client can call most of the Open Payments APIs, it must receive a grant from the appropriate authorization server.
The code snippets below let an authenticated client request a grant for an outgoing payment. The request to the authorization server must indicate the outgoing-payment and the actions the client wants to take at the resource server.
Interactive grants
Section titled “Interactive grants”Outgoing payments require explicit consent, typically by the client’s user, before a grant can be issued. Consent is obtained through an interactive grant.
Any authorization server that issues interactive grants must integrate with an identity provider (IdP). When a client requests the outgoing payment grant, the authorization server provides the client with the IdP URI to redirect to.
Before you begin
Section titled “Before you begin”We recommend creating a wallet account on the test wallet. Creating an account allows you to test your client against the Open Payments APIs by using an ILP-enabled wallet funded with play money.
Request an outgoing payment grant
Section titled “Request an outgoing payment grant”Prerequisites
Initial configuration
If you’re using JavaScript, only do the first step.
-
Add
"type": "module"topackage.json. -
Add the following to
tsconfig.json{"compilerOptions": {"target": "ES2022","module": "ES2022"}}
Generate without an interval
Section titled “Generate without an interval”// Import dependenciesimport { createAuthenticatedClient, isPendingGrant} from '@interledger/open-payments'
// Initialize clientconst client = await createAuthenticatedClient({ walletAddressUrl: CLIENT_WALLET_ADDRESS, privateKey: PRIVATE_KEY_PATH, keyId: KEY_ID})
// Get wallet address informationconst walletAddress = await client.walletAddress.get({ url: WALLET_ADDRESS})
// Request outgoing payment grantconst grant = await client.grant.request( { url: walletAddress.authServer }, { access_token: { access: [ { identifier: walletAddress.id, type: 'outgoing-payment', actions: ['list', 'list-all', 'read', 'read-all', 'create'], limits: { debitAmount: { assetCode: quote.debitAmount.assetCode, assetScale: quote.debitAmount.assetScale, value: quote.debitAmount.value } } } ] }, interact: { start: ['redirect'], finish: { method: 'redirect', uri: 'http://localhost:3344', nonce: NONCE } } })
// Check grant stateif (!isPendingGrant(grant)) { throw new Error('Expected pending/interactive grant')}
// Outputconsole.log('Please interact at the following URL:', grant.interact.redirect)console.log('CONTINUE_ACCESS_TOKEN =', grant.continue.access_token.value)console.log('CONTINUE_URI =', grant.continue.uri)Generate with an interval
Section titled “Generate with an interval”// Import dependenciesimport { createAuthenticatedClient, isPendingGrant} from '@interledger/open-payments'
// Initialize clientconst client = await createAuthenticatedClient({ walletAddressUrl: CLIENT_WALLET_ADDRESS, privateKey: PRIVATE_KEY_PATH, keyId: KEY_ID})
// Get wallet address informationconst walletAddress = await client.walletAddress.get({ url: WALLET_ADDRESS})
// Request outgoing payment grantconst grant = await client.grant.request( { url: walletAddress.authServer }, { access_token: { access: [ { identifier: walletAddress.id, type: 'outgoing-payment', actions: ['list', 'list-all', 'read', 'read-all', 'create'], limits: { debitAmount: { assetCode: quote.debitAmount.assetCode, assetScale: quote.debitAmount.assetScale, value: quote.debitAmount.value }, interval: 'R/2016-08-24T08:00:00Z/P1D' } } ] }, interact: { start: ['redirect'], finish: { method: 'redirect', uri: 'http://localhost:3344', nonce: NONCE } } })
// Check grant stateif (!isPendingGrant(grant)) { throw new Error('Expected pending/interactive grant')}
// Outputconsole.log('Please interact at the following URL:', grant.interact.redirect)console.log('CONTINUE_ACCESS_TOKEN =', grant.continue.access_token.value)console.log('CONTINUE_URI =', grant.continue.uri)For TypeScript, run tsx path/to/directory/index.ts. View full TS source
For JavaScript, run node path/to/directory/index.js. View full JS source
Prerequisites
// Import dependenciesuse open_payments::client::api::UnauthenticatedResources;use open_payments::client::AuthenticatedResources;use open_payments::snippets::utils::{create_authenticated_client, get_env_var, load_env};use open_payments::types::{ auth::{ AccessItem, AccessTokenRequest, GrantRequest, InteractFinish, InteractRequest, LimitsOutgoing, OutgoingPaymentAction, }, GrantResponse,};use uuid::Uuid;
// Initialize client// Authenticated client can be also used for unauthenticated resourceslet client = create_authenticated_client()?;
// Get wallet address informationlet wallet_address_url = get_env_var("WALLET_ADDRESS_URL")?;let wallet_address = client.wallet_address().get(&wallet_address_url).await?;
// Request outgoing payment grantlet quote_url = get_env_var("QUOTE_URL")?;let access_token = get_env_var("QUOTE_ACCESS_TOKEN")?;let quote = client.quotes().get("e_url, Some(&access_token)).await?;
let wallet_id = &wallet_address.id;let grant_request = GrantRequest::new( AccessTokenRequest { access: vec![AccessItem::OutgoingPayment { actions: vec![ OutgoingPaymentAction::Read, OutgoingPaymentAction::ReadAll, OutgoingPaymentAction::List, OutgoingPaymentAction::Create, ], identifier: wallet_id.to_string(), limits: Some(LimitsOutgoing { receiver: None, debit_amount: Some(quote.debit_amount), receive_amount: None, interval: None, }), }], }, Some(InteractRequest { start: vec!["redirect".to_string()], finish: Some(InteractFinish { method: "redirect".to_string(), uri: "http://localhost".to_string(), nonce: Uuid::new_v4().to_string(), }), }),);
println!( "Grant request JSON: {}", serde_json::to_string_pretty(&grant_request)?);
let response = client .grant() .request(&wallet_address.auth_server, &grant_request) .await?;
// Outputmatch response { GrantResponse::WithToken { access_token, .. } => { println!("Received access token: {:#?}", access_token.value); println!( "Received access token manage URL: {:#?}", access_token.manage ); } GrantResponse::WithInteraction { interact, continue_, } => { println!("Received interact: {interact:#?}"); println!("Received continue: {continue_:#?}"); }}Prerequisites
Generate without an interval
Section titled “Generate without an interval”// Import dependenciesuse OpenPayments\AuthClient;use OpenPayments\Config\Config;
// Initialize client$config = new Config( $WALLET_ADDRESS, $PRIVATE_KEY, $KEY_ID);$opClient = new AuthClient($config);
// Get wallet address information$wallet = $opClient->walletAddress()->get([ 'url' => $config->getWalletAddressUrl()]);
// Request outgoing payment grant$grant = $opClient->grant()->request( [ 'url' => $wallet->authServer ], [ 'access_token' => [ 'access' => [ [ 'type' => 'outgoing-payment', 'actions' => ['list', 'list-all', 'read', 'read-all', 'create'], 'identifier' => $wallet->id, 'limits' => [ 'receiver' => $INCOMING_PAYMENT_URL, //optional 'debitAmount' => [ 'assetCode' => 'USD', 'assetScale' => 2, 'value' => "130", ] ], ] ] ], 'client' => $config->getWalletAddressUrl(), 'interact' => [ 'start' => ["redirect"], 'finish' => [ 'method' => "redirect", 'uri' => 'https://localhost/?paymentId=123423', 'nonce' => "1234567890", ], ] ]);
// Check grant stateif (!$grant instanceof \OpenPayments\Models\PendingGrant) { throw new \Error('Expected interactive grant');}
// Outputecho 'Please interact at the following URL: ' . $grant->interact->redirect . PHP_EOL;echo 'CONTINUE_ACCESS_TOKEN = ' . $grant->continue->access_token->value . PHP_EOL;echo 'CONTINUE_URI = ' . $grant->continue->uri . PHP_EOL;echo 'GRANT OBJECT: ' . PHP_EOL . print_r($grant, true);Generate with an interval and output
Section titled “Generate with an interval and output”// Import dependenciesuse OpenPayments\AuthClient;use OpenPayments\Config\Config;
// Initialize client$config = new Config( $WALLET_ADDRESS, $PRIVATE_KEY, $KEY_ID);$opClient = new AuthClient($config);
// Get wallet address information$wallet = $opClient->walletAddress()->get([ 'url' => $config->getWalletAddressUrl()]);
// Request outgoing payment grant$grant = $opClient->grant()->request( [ 'url' => $wallet->authServer ], [ 'access_token' => [ 'access' => [ [ 'type' => 'outgoing-payment', 'actions' => ['list', 'list-all', 'read', 'read-all', 'create'], 'identifier' => $wallet->id, 'limits' => [ 'debitAmount' => [ 'assetCode' => 'USD', 'assetScale' => 2, 'value' => "132", ], 'interval' => 'R/2025-04-22T08:00:00Z/P1D', ], ] ] ], 'client' => $config->getWalletAddressUrl(), 'interact' => [ 'start' => ["redirect"], 'finish' => [ 'method' => "redirect", 'uri' => 'https://localhost/?paymentId=123423', 'nonce' => "1234567890", ], ] ]);
// Check grant stateif (!$grant instanceof \OpenPayments\Models\PendingGrant) { throw new \Error('Expected interactive grant');}
// Outputecho 'Please interact at the following URL: ' . $grant->interact->redirect . PHP_EOL;echo 'CONTINUE_ACCESS_TOKEN = ' . $grant->continue->access_token->value . PHP_EOL;echo 'CONTINUE_URI = ' . $grant->continue->uri . PHP_EOL;echo 'GRANT OBJECT: ' . PHP_EOL . print_r($grant, true);Prerequisites
package main
// Import dependenciesimport ( "context" "encoding/json" "fmt" "log"
op "github.com/interledger/open-payments-go" as "github.com/interledger/open-payments-go/generated/authserver")
func main() { // Initialize client client, err := op.NewAuthenticatedClient(WALLET_ADDRESS_URL, PRIVATE_KEY_BASE_64, KEY_ID) if err != nil { log.Fatalf("Error creating authenticated client: %v\n", err) }
// Get wallet address information walletAddress, err := client.WalletAddress.Get(context.TODO(), op.WalletAddressGetParams{ URL: WALLET_ADDRESS_URL, }) if err != nil { log.Fatalf("Error fetching wallet address: %v\n", err) }
// Request outgoing payment grant outgoingAccess := as.AccessOutgoing{ Type: as.OutgoingPayment, Actions: []as.AccessOutgoingActions{ as.AccessOutgoingActionsCreate, as.AccessOutgoingActionsRead, as.AccessOutgoingActionsList, }, Identifier: *walletAddress.Id, } accessItem := as.AccessItem{} if err := accessItem.FromAccessOutgoing(outgoingAccess); err != nil { log.Fatalf("Error creating AccessItem: %v\n", err) } accessToken := struct { Access as.Access `json:"access"` }{ Access: []as.AccessItem{accessItem}, } interact := &as.InteractRequest{ Start: []as.InteractRequestStart{as.InteractRequestStartRedirect}, }
grant, err := client.Grant.Request(context.TODO(), op.GrantRequestParams{ URL: *walletAddress.AuthServer, RequestBody: as.GrantRequestWithAccessToken{ AccessToken: accessToken, Interact: interact, }, }) if err != nil { log.Fatalf("Error requesting grant: %v\n", err) }
// Check grant state if !grant.IsInteractive() { log.Fatalf("Expected interactive grant") }
// Output grantJSON, err := json.MarshalIndent(grant, "", " ") if err != nil { log.Fatalf("Error marshaling grant: %v\n", err) } fmt.Println("GRANT:", string(grantJSON)) fmt.Println("Please interact at the following URL:", grant.Interact.Redirect) fmt.Println("CONTINUE_ACCESS_TOKEN =", grant.Continue.AccessToken.Value) fmt.Println("CONTINUE_URI =", grant.Continue.Uri)}Prerequisites
// Import dependenciesimport org.interledger.openpayments.httpclient.OpenPaymentsHttpClient;import org.interledger.openpayments.IOpenPaymentsClient;
// Initialize clientvar client = OpenPaymentsHttpClient.defaultClient("WalletAddress","PrivateKeyPEM","KeyId");
// Get wallet address informationvar senderWallet = client.walletAddress().get("https://cloudninebank.example.com/customer");var receiverWallet = client.walletAddress().get("https://cloudninebank.example.com/merchant");
// Create an incoming paymentvar grantRequest = client.auth().grant().incomingPayment(receiverWallet);var incomingPayment = client.payment().createIncoming(receiverWallet, grantRequest, BigDecimal.valueOf(11.25));
// Create a quotevar quoteRequest = client.auth().grant().quote(senderWallet);var quote = client.quote().create(quoteRequest.getAccess().getToken(), senderWallet, incomingPayment, Optional.empty(), Optional.empty());
var urlToOpen = "https://example.com/redirect?paymentId=1234";
// Create an outgoing payment from quotevar opContinueInteract = client.auth().grant().continuation(senderWallet,quote.getDebitAmount(),URI.create(urlToOpen),"test");
// Outputlog.info("OUTGOING_PAYMENT_GRANT: {}", opContinueInteract);Prerequisites
Generate without an interval
Section titled “Generate without an interval”// Import dependenciesusing Microsoft.Extensions.DependencyInjection;using Newtonsoft.Json;using OpenPayments.Sdk.Clients;using OpenPayments.Sdk.Extensions;using OpenPayments.Sdk.Generated.Auth;using OpenPayments.Sdk.Generated.Resource;using OpenPayments.Sdk.HttpSignatureUtils;using Amount = OpenPayments.Sdk.Generated.Auth.Amount;
// Initialize clientvar client = new ServiceCollection() .UseOpenPayments(opts => { opts.UseAuthenticatedClient = true; opts.KeyId = CLIENT_ID; opts.PrivateKey = KeyUtils.LoadPem(CLIENT_SECRET); opts.ClientUrl = new Uri(CLIENT_WALLET_ADDRESS); }) .BuildServiceProvider() .GetRequiredService<IAuthenticatedClient>();
// Get wallet address informationvar walletAddress = await client.GetWalletAddressAsync(WALLET_ADDRESS);
// Request quote grantvar grant = await client.RequestGrantAsync( new RequestArgs() { Url = walletAddress.AuthServer }, new GrantCreateBodyWithInteract { AccessToken = new AccessToken { Access = [ new OutgoingAccess { Actions = [Actions.List, Actions.ListAll, Actions.Read, Actions.ReadAll, Actions.Create], Identifier = walletAddress.Id, Limits = new OutgoingAccessLimits { DebitAmount = new Amount(quote.DebitAmount.Value, quote.DebitAmount.AssetCode, quote.DebitAmount.AssetScale), }, }, ], }, Interact = new InteractRequest() { Start = [Start.Redirect], Finish = new Finish() { Method = FinishMethod.Redirect, Uri = new Uri("http://localhost:3344"), Nonce = NONCE } }, });
// Check grant stateif (grant.Interact == null){ throw new Exception("Expected interactive grant");}
// OutputConsole.WriteLine($"Please interact at the following URL: {grant.Interact.Redirect}");Console.WriteLine($"CONTINUE_ACCESS_TOKEN = {grant.Continue.AccessToken.Value}");Console.WriteLine($"CONTINUE_URI = {grant.Continue.Uri}");Generate with an interval
Section titled “Generate with an interval”// Import dependenciesusing Microsoft.Extensions.DependencyInjection;using Newtonsoft.Json;using OpenPayments.Sdk.Clients;using OpenPayments.Sdk.Extensions;using OpenPayments.Sdk.Generated.Auth;using OpenPayments.Sdk.Generated.Resource;using OpenPayments.Sdk.HttpSignatureUtils;using Amount = OpenPayments.Sdk.Generated.Auth.Amount;
// Initialize clientvar client = new ServiceCollection() .UseOpenPayments(opts => { opts.UseAuthenticatedClient = true; opts.KeyId = CLIENT_ID; opts.PrivateKey = KeyUtils.LoadPem(CLIENT_SECRET); opts.ClientUrl = new Uri(CLIENT_WALLET_ADDRESS); }) .BuildServiceProvider() .GetRequiredService<IAuthenticatedClient>();
// Get wallet address informationvar walletAddress = await client.GetWalletAddressAsync(WALLET_ADDRESS);
// Request quote grantvar grant = await client.RequestGrantAsync( new RequestArgs { Url = walletAddress.AuthServer }, new GrantCreateBodyWithInteract { AccessToken = new AccessToken { Access = [ new OutgoingAccess { Actions = [Actions.List, Actions.ListAll, Actions.Read, Actions.ReadAll, Actions.Create], Identifier = walletAddress.Id, Limits = new OutgoingAccessLimits { DebitAmount = new Amount(quote.DebitAmount.Value, quote.DebitAmount.AssetCode, quote.DebitAmount.AssetScale), Interval = "R/2016-08-24T08:00:00Z/P1D" }, }, ], }, Interact = new InteractRequest { Start = [Start.Redirect], Finish = new Finish { Method = FinishMethod.Redirect, Uri = new Uri("http://localhost:3344"), Nonce = NONCE } }, });
// Check grant stateif (grant.Interact == null){ throw new Exception("Expected interactive grant");}
// OutputConsole.WriteLine($"Please interact at the following URL: {grant.Interact.Redirect}");Console.WriteLine($"CONTINUE_ACCESS_TOKEN = {grant.Continue.AccessToken.Value}");Console.WriteLine($"CONTINUE_URI = {grant.Continue.Uri}");Create a subject verification request
Section titled “Create a subject verification request”The subject field lets a client ask the authorization server to verify information about a subject as part of a grant request. For example, a client can use it to confirm that a wallet address identifies a particular account holder. The request must include subject.sub_ids with the subject’s id and the format (uri) the client accepts.
Subject verification requests are always interactive grants. The request must include an interact object, and the user consents through the identity provider (IdP) flow. After the user completes the interaction, the grant continuation response can include a subject object with the verified sub_ids.
Prerequisites
Initial configuration
If you’re using JavaScript, only do the first step.
-
Add
"type": "module"topackage.json. -
Add the following to
tsconfig.json{"compilerOptions": {"target": "ES2022","module": "ES2022"}}
// Import dependenciesimport { createAuthenticatedClient, isPendingGrant} from '@interledger/open-payments'
// Initialize clientconst client = await createAuthenticatedClient({ walletAddressUrl: CLIENT_WALLET_ADDRESS, privateKey: PRIVATE_KEY_PATH, keyId: KEY_ID})
// Get wallet address informationconst walletAddress = await client.walletAddress.get({ url: WALLET_ADDRESS})
// Request subject verification grant// TODO(docs): confirm `subject` support in TypeScript SDKconst grant = await client.grant.request( { url: walletAddress.authServer }, { subject: { sub_ids: [ { id: walletAddress.id, format: 'uri' } ] }, client: CLIENT_WALLET_ADDRESS, interact: { start: ['redirect'], finish: { method: 'redirect', uri: 'http://localhost:3344', nonce: NONCE } } })
// Check grant stateif (!isPendingGrant(grant)) { throw new Error('Expected pending/interactive grant')}
// Outputconsole.log('Please interact at the following URL:', grant.interact.redirect)console.log('CONTINUE_ACCESS_TOKEN =', grant.continue.access_token.value)console.log('CONTINUE_URI =', grant.continue.uri)For TypeScript, run tsx path/to/directory/index.ts.
Prerequisites
// Import dependenciesuse open_payments::client::api::UnauthenticatedResources;use open_payments::client::AuthenticatedResources;use open_payments::snippets::utils::{create_authenticated_client, get_env_var, load_env};use open_payments::types::GrantResponse;use serde_json::json;use uuid::Uuid;
// Initialize client// Authenticated client can be also used for unauthenticated resourceslet client = create_authenticated_client()?;
// Get wallet address informationlet wallet_address_url = get_env_var("WALLET_ADDRESS_URL")?;let client_wallet_address_url = get_env_var("CLIENT_WALLET_ADDRESS_URL")?;let wallet_address = client.wallet_address().get(&wallet_address_url).await?;
// Request subject verification grant// TODO(docs): confirm `subject` support in Rust SDKlet grant_request = json!({ "subject": { "sub_ids": [ { "id": wallet_address_url, "format": "uri" } ] }, "client": client_wallet_address_url, "interact": { "start": ["redirect"], "finish": { "method": "redirect", "uri": "http://localhost:3344", "nonce": Uuid::new_v4().to_string() } }});
let response = client .grant() .request(&wallet_address.auth_server, &grant_request) .await?;
// Check grant statematch response { GrantResponse::WithInteraction { interact, continue_, } => { println!("Please interact at the following URL: {interact:#?}"); println!("CONTINUE_ACCESS_TOKEN = {:#?}", continue_.access_token.value); println!("CONTINUE_URI = {:#?}", continue_.uri); } _ => { panic!("Expected pending/interactive grant"); }}Prerequisites
// Import dependenciesuse OpenPayments\AuthClient;use OpenPayments\Config\Config;
// Initialize client$config = new Config( $WALLET_ADDRESS, $PRIVATE_KEY, $KEY_ID);$opClient = new AuthClient($config);
// Get wallet address information$wallet = $opClient->walletAddress()->get([ 'url' => $config->getWalletAddressUrl()]);
// Request subject verification grant// TODO(docs): confirm `subject` support in PHP SDK$grant = $opClient->grant()->request( [ 'url' => $wallet->authServer ], [ 'subject' => [ 'sub_ids' => [ [ 'id' => $wallet->id, 'format' => 'uri', ], ], ], 'client' => $config->getWalletAddressUrl(), 'interact' => [ 'start' => ["redirect"], 'finish' => [ 'method' => "redirect", 'uri' => 'https://localhost/?paymentId=123423', 'nonce' => "1234567890", ], ] ]);
// Check grant stateif (!$grant instanceof \OpenPayments\Models\PendingGrant) { throw new \Error('Expected interactive grant');}
// Outputecho 'Please interact at the following URL: ' . $grant->interact->redirect . PHP_EOL;echo 'CONTINUE_ACCESS_TOKEN = ' . $grant->continue->access_token->value . PHP_EOL;echo 'CONTINUE_URI = ' . $grant->continue->uri . PHP_EOL;echo 'GRANT OBJECT: ' . PHP_EOL . print_r($grant, true);Prerequisites
package main
// Import dependenciesimport ( "context" "encoding/json" "fmt" "log"
op "github.com/interledger/open-payments-go")
func main() { // Initialize client client, err := op.NewAuthenticatedClient(WALLET_ADDRESS_URL, PRIVATE_KEY_BASE_64, KEY_ID) if err != nil { log.Fatalf("Error creating authenticated client: %v\n", err) }
// Get wallet address information walletAddress, err := client.WalletAddress.Get(context.TODO(), op.WalletAddressGetParams{ URL: WALLET_ADDRESS_URL, }) if err != nil { log.Fatalf("Error fetching wallet address: %v\n", err) }
// Request subject verification grant // TODO(docs): confirm `subject` support in Go SDK requestBody := map[string]interface{}{ "subject": map[string]interface{}{ "sub_ids": []map[string]string{ { "id": *walletAddress.Id, "format": "uri", }, }, }, "client": CLIENT_WALLET_ADDRESS_URL, "interact": map[string]interface{}{ "start": []string{"redirect"}, "finish": map[string]string{ "method": "redirect", "uri": "http://localhost:3344", "nonce": NONCE, }, }, }
grant, err := client.Grant.Request(context.TODO(), op.GrantRequestParams{ URL: *walletAddress.AuthServer, RequestBody: requestBody, }) if err != nil { log.Fatalf("Error requesting grant: %v\n", err) }
// Check grant state if !grant.IsInteractive() { log.Fatalf("Expected interactive grant") }
// Output grantJSON, err := json.MarshalIndent(grant, "", " ") if err != nil { log.Fatalf("Error marshaling grant: %v\n", err) } fmt.Println("GRANT:", string(grantJSON)) fmt.Println("Please interact at the following URL:", grant.Interact.Redirect) fmt.Println("CONTINUE_ACCESS_TOKEN =", grant.Continue.AccessToken.Value) fmt.Println("CONTINUE_URI =", grant.Continue.Uri)}Prerequisites
// Import dependenciesimport org.interledger.openpayments.httpclient.OpenPaymentsHttpClient;import org.interledger.openpayments.IOpenPaymentsClient;
// Initialize clientvar client = OpenPaymentsHttpClient.defaultClient( "WalletAddress", "PrivateKeyPEM", "KeyId");
// Get wallet address informationvar walletAddress = client.walletAddress().get(WALLET_ADDRESS);
// Request subject verification grant// TODO(docs): confirm `subject` support in Java SDK// {// "subject": { "sub_ids": [ { "id": "<wallet-address>", "format": "uri" } ] },// "client": "<client-wallet-address>",// "interact": {// "start": ["redirect"],// "finish": { "method": "redirect", "uri": "http://localhost:3344", "nonce": "<nonce>" }// }// }var grant = client.auth().grant().request( walletAddress.getAuthServer(), Map.of( "subject", Map.of( "sub_ids", List.of( Map.of("id", walletAddress.getId(), "format", "uri") ) ), "client", CLIENT_WALLET_ADDRESS, "interact", Map.of( "start", List.of("redirect"), "finish", Map.of( "method", "redirect", "uri", "http://localhost:3344", "nonce", NONCE ) ) ));
// Check grant state// TODO(docs): confirm pending-grant check API in Java SDK
// Outputlog.info("Please interact at the following URL: {}", grant.getInteract().getRedirect());log.info("CONTINUE_ACCESS_TOKEN = {}", grant.getContinue().getAccessToken().getValue());log.info("CONTINUE_URI = {}", grant.getContinue().getUri());Prerequisites
// Import dependenciesusing Microsoft.Extensions.DependencyInjection;using OpenPayments.Sdk.Clients;using OpenPayments.Sdk.Extensions;using OpenPayments.Sdk.Generated.Auth;using OpenPayments.Sdk.HttpSignatureUtils;
// Initialize clientvar client = new ServiceCollection() .UseOpenPayments(opts => { opts.UseAuthenticatedClient = true; opts.KeyId = CLIENT_ID; opts.PrivateKey = KeyUtils.LoadPem(CLIENT_SECRET); opts.ClientUrl = new Uri(CLIENT_WALLET_ADDRESS); }) .BuildServiceProvider() .GetRequiredService<IAuthenticatedClient>();
// Get wallet address informationvar walletAddress = await client.GetWalletAddressAsync(WALLET_ADDRESS);
// Request subject verification grant// TODO(docs): confirm `subject` support in .NET SDKvar grant = await client.RequestGrantAsync( new RequestArgs() { Url = walletAddress.AuthServer }, new { Subject = new { SubIds = new[] { new { Id = WALLET_ADDRESS, Format = "uri" } } }, Client = CLIENT_WALLET_ADDRESS, Interact = new InteractRequest() { Start = [Start.Redirect], Finish = new Finish() { Method = FinishMethod.Redirect, Uri = new Uri("http://localhost:3344"), Nonce = NONCE } }, });
// Check grant stateif (grant.Interact == null){ throw new Exception("Expected interactive grant");}
// OutputConsole.WriteLine($"Please interact at the following URL: {grant.Interact.Redirect}");Console.WriteLine($"CONTINUE_ACCESS_TOKEN = {grant.Continue.AccessToken.Value}");Console.WriteLine($"CONTINUE_URI = {grant.Continue.Uri}");