Setup Script Template
Once you have all inputs from Step 3, generate a single Python script called setup_payments.py that executes all the following steps automatically without human intervention. Write the script, then execute it.
The script must:
- Store payment provider credentials in AgentCore Identity
- Create the IAM execution role with trust policy and permissions
- Wait for IAM propagation (15 seconds)
- Create the Payment Manager and wait for READY status
- Create the Payment Connector
- Create the Payment Instrument (wallet)
- Print a summary of all created resources and next steps
Template
Substitute the developer's inputs into the configuration section:
"""
AgentCore Payments Setup Script
Generated by the payments skill. Executes all non-interactive setup steps.
NAMING RULES:
- Resource names (credential provider, manager, connector): lowercase alphanumeric + hyphens only.
NO underscores, NO dots, NO uppercase. Pattern: [a-z0-9]([a-z0-9-]*[a-z0-9])?
- The paymentManagerId (returned by create) is used for CP get/list operations.
- The paymentManagerArn (returned by create) is used for DP operations (instrument, session, process).
- create_payment_session requires userId parameter.
"""
import boto3
import json
import uuid
import time
import os
# === CONFIGURATION (from developer inputs) ===
REGION = "<REGION>" # e.g., "ap-southeast-2"
ACCOUNT_ID = "<ACCOUNT_ID>" # e.g., "123456789012"
PROVIDER = "<PROVIDER>" # "CoinbaseCDP" or "StripePrivy"
END_USER_EMAIL = "<END_USER_EMAIL>" # e.g., "developer@example.com"
RESOURCE_PREFIX = "paymentspoc" # prefix for all resource names
# Read credentials from environment variables (NOT from file directly).
# Run `source .env.payments` in your terminal before executing this script.
# Do NOT pass credentials through the agent — they must stay local.
# For Coinbase:
COINBASE_API_KEY_ID = os.environ.get("COINBASE_API_KEY_ID", "")
COINBASE_API_KEY_SECRET = os.environ.get("COINBASE_API_KEY_SECRET", "")
COINBASE_WALLET_SECRET = os.environ.get("COINBASE_WALLET_SECRET", "")
# For Stripe:
AUTH_PRIVATE_KEY = os.environ.get("AUTH_PRIVATE_KEY", "")
AUTH_ID = os.environ.get("AUTH_ID", "")
PRIVY_APP_ID = os.environ.get("PRIVY_APP_ID", "")
PRIVY_APP_SECRET = os.environ.get("PRIVY_APP_SECRET", "")
# === CLIENTS ===
iam = boto3.client("iam")
cp_client = boto3.client("bedrock-agentcore-control", region_name=REGION)
dp_client = boto3.client("bedrock-agentcore", region_name=REGION)
print("=" * 60)
print("AgentCore Payments Setup")
print("=" * 60)
# === STEP 1: Store credentials ===
print("\n[1/6] Storing payment provider credentials...")
cred_name = f"{RESOURCE_PREFIX}-creds"
def create_credential_provider_with_retry(name, vendor, config, max_retries=5):
"""Create credential provider, appending a numeric suffix if name already exists."""
for attempt in range(max_retries):
unique_name = name if attempt == 0 else f"{name}-{attempt}"
try:
if vendor == "CoinbaseCDP":
resp = cp_client.create_payment_credential_provider(
name=unique_name,
credentialProviderVendor=vendor,
providerConfigurationInput={"coinbaseCdpConfiguration": config}
)
elif vendor == "StripePrivy":
resp = cp_client.create_payment_credential_provider(
name=unique_name,
credentialProviderVendor=vendor,
providerConfigurationInput={"stripePrivyConfiguration": config}
)
print(f" (Using name: {unique_name})")
return resp
except Exception as e:
if "already exists" in str(e).lower() or "conflict" in str(e).lower():
print(f" Name '{unique_name}' already exists, trying with suffix...")
continue
raise
raise Exception(f"Failed to create credential provider after {max_retries} attempts")
if PROVIDER == "CoinbaseCDP":
cred_config = {
"apiKeyId": COINBASE_API_KEY_ID,
"apiKeySecret": COINBASE_API_KEY_SECRET,
"walletSecret": COINBASE_WALLET_SECRET
}
elif PROVIDER == "StripePrivy":
cred_config = {
"appId": PRIVY_APP_ID,
"appSecret": PRIVY_APP_SECRET,
"authorizationPrivateKey": AUTH_PRIVATE_KEY,
"authorizationId": AUTH_ID
}
cred_resp = create_credential_provider_with_retry(cred_name, PROVIDER, cred_config)
credential_provider_arn = cred_resp["credentialProviderArn"]
print(f" OK Credential Provider ARN: {credential_provider_arn}")
# === STEP 2: Create IAM role ===
print("\n[2/6] Creating IAM service role...")
base_role_name = f"AgentCorePayments-{RESOURCE_PREFIX}"
def create_role_with_retry(base_name, max_retries=5):
"""Create IAM role, appending a numeric suffix if name already exists."""
for attempt in range(max_retries):
unique_name = base_name if attempt == 0 else f"{base_name}-{attempt}"
try:
iam.create_role(
RoleName=unique_name,
AssumeRolePolicyDocument=json.dumps({
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "bedrock-agentcore.amazonaws.com"},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {"aws:SourceAccount": ACCOUNT_ID},
"ArnLike": {"aws:SourceArn": f"arn:aws:bedrock-agentcore:{REGION}:{ACCOUNT_ID}:payment-manager/{RESOURCE_PREFIX}-*"}
}
}]
}),
Description="Service role for AgentCore Payments"
)
print(f" (Using role name: {unique_name})")
return unique_name
except iam.exceptions.EntityAlreadyExistsException:
print(f" Role '{unique_name}' already exists, trying with suffix...")
continue
raise Exception(f"Failed to create role after {max_retries} attempts")
role_name = create_role_with_retry(base_role_name)
iam.put_role_policy(
RoleName=role_name,
PolicyName="PaymentsResourceRetrievalPolicy",
PolicyDocument=json.dumps({
"Version": "2012-10-17",
"Statement": [
{
"Sid": "WorkloadIdentity",
"Effect": "Allow",
"Action": [
"bedrock-agentcore:CreateWorkloadIdentity",
"bedrock-agentcore:GetWorkloadAccessToken",
"bedrock-agentcore:GetResourcePaymentToken"
],
"Resource": [
f"arn:aws:bedrock-agentcore:{REGION}:{ACCOUNT_ID}:token-vault/default",
f"arn:aws:bedrock-agentcore:{REGION}:{ACCOUNT_ID}:token-vault/default/paymentcredentialprovider/*",
f"arn:aws:bedrock-agentcore:{REGION}:{ACCOUNT_ID}:workload-identity-directory/default",
f"arn:aws:bedrock-agentcore:{REGION}:{ACCOUNT_ID}:workload-identity-directory/default/workload-identity/*"
]
},
{
"Sid": "SecretsAccess",
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": f"arn:aws:secretsmanager:{REGION}:{ACCOUNT_ID}:secret:bedrock-agentcore-identity*"
}
]
})
)
role_arn = f"arn:aws:iam::{ACCOUNT_ID}:role/{role_name}"
print(f" OK Role ARN: {role_arn}")
print(" Waiting 15s for IAM propagation...")
time.sleep(15)
# === STEP 3: Create Payment Manager ===
print("\n[3/6] Creating Payment Manager...")
mgr_resp = cp_client.create_payment_manager(
name=RESOURCE_PREFIX,
description="Payment manager created by AgentCore Payments skill",
authorizerType="AWS_IAM",
roleArn=role_arn,
clientToken=str(uuid.uuid4())
)
payment_manager_arn = mgr_resp["paymentManagerArn"]
manager_id = mgr_resp["paymentManagerId"]
print(f" OK Payment Manager ARN: {payment_manager_arn}")
# Wait for READY
for i in range(12):
status_resp = cp_client.get_payment_manager(paymentManagerId=manager_id)
if status_resp["status"] == "READY":
break
time.sleep(5)
if status_resp["status"] != "READY":
raise Exception(
f"Payment Manager did not reach READY status after 60s "
f"(current: {status_resp['status']}). Check CloudTrail for errors."
)
print(f" OK Status: {status_resp['status']}")
# === STEP 4: Create Payment Connector ===
print("\n[4/6] Creating Payment Connector...")
connector_config_key = "coinbaseCDP" if PROVIDER == "CoinbaseCDP" else "stripePrivy"
conn_resp = cp_client.create_payment_connector(
paymentManagerId=manager_id,
name=f"{RESOURCE_PREFIX}connector",
description=f"{PROVIDER} connector",
type=PROVIDER,
credentialProviderConfigurations=[{
connector_config_key: {"credentialProviderArn": credential_provider_arn}
}],
clientToken=str(uuid.uuid4())
)
connector_id = conn_resp["paymentConnectorId"]
print(f" OK Connector ID: {connector_id}")
# === STEP 5: Create Payment Instrument ===
print("\n[5/6] Creating Payment Instrument (wallet)...")
user_id = f"{RESOURCE_PREFIX}-user"
instr_resp = dp_client.create_payment_instrument(
paymentManagerArn=payment_manager_arn,
paymentConnectorId=connector_id,
userId=user_id,
paymentInstrumentType="EMBEDDED_CRYPTO_WALLET",
paymentInstrumentDetails={
"embeddedCryptoWallet": {
"network": "ETHEREUM",
"linkedAccounts": [
{"email": {"emailAddress": END_USER_EMAIL}}
]
}
},
clientToken=str(uuid.uuid4())
)
instrument_data = instr_resp.get("paymentInstrument", instr_resp)
payment_instrument_id = instrument_data["paymentInstrumentId"]
wallet_details = instrument_data.get("paymentInstrumentDetails", {}).get("embeddedCryptoWallet", {})
wallet_address = wallet_details.get("walletAddress", "pending")
redirect_url = wallet_details.get("redirectUrl", None)
print(f" OK Instrument ID: {payment_instrument_id}")
print(f" OK Wallet Address: {wallet_address}")
# === STEP 6: Create Payment Session ===
print("\n[6/6] Creating Payment Session...")
session_resp = dp_client.create_payment_session(
paymentManagerArn=payment_manager_arn,
userId=user_id,
expiryTimeInMinutes=60
)
payment_session_id = session_resp["paymentSession"]["paymentSessionId"]
print(f" OK Session ID: {payment_session_id}")
# === SUMMARY ===
print("\n" + "=" * 60)
print("SETUP COMPLETE")
print("=" * 60)
print(f"""
Resources created:
Payment Manager ARN: {payment_manager_arn}
Connector ID: {connector_id}
Instrument ID: {payment_instrument_id}
Wallet Address: {wallet_address}
Session ID: {payment_session_id}
User ID: {user_id}
Region: {REGION}
Environment variables for your agent:
export PAYMENT_MANAGER_ARN="{payment_manager_arn}"
export PAYMENT_INSTRUMENT_ID="{payment_instrument_id}"
export PAYMENT_SESSION_ID="{payment_session_id}"
export PAYMENT_USER_ID="{user_id}"
export AWS_REGION="{REGION}"
""")
print("\nMANUAL STEPS REQUIRED:\n")
# Step 1: Delegation — provider-specific
if PROVIDER == "CoinbaseCDP":
print(f"""1. DELEGATION — Grant the agent permission to spend from the wallet:
Visit: {redirect_url}
Log in with: {END_USER_EMAIL}
Grant permissions to the wallet address: {wallet_address}
""")
elif PROVIDER == "StripePrivy":
print(f"""1. DELEGATION — Enable delegation on the embedded wallet:
a. Set up a frontend using the Privy frontend SDK:
https://github.com/privy-io/aws-agentcore-sdk
b. Log in with the end user email: {END_USER_EMAIL}
c. Approve delegation for the wallet address: {wallet_address}
""")
# Step 2: Funding — same for both providers
print(f"""2. FUNDING — Send testnet USDC to the wallet:
Go to: https://faucet.circle.com/
Select: Base Sepolia
Paste wallet address: {wallet_address}
""")After executing the script
- Tell the developer to run
source .env.paymentsbefore executing the script - Print the summary to the developer
- Tell them to complete the two manual steps (delegation + funding) for the provider they chose
- Do NOT reference the other provider's flow — only show steps for the provider in use
- Wait for them to confirm before proceeding to Step 5 (wiring)