Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions packages/cli/src/__tests__/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,61 @@ describe('production mode', () => {
});
});

it('parses a single --metadata flag with comma-separated pairs', async () => {
setNextResponse(200, BASE_REQUEST);

const result = await runProdCli(
'spend-request',
'create',
'--payment-method-id',
'pd_prod_test',
'--merchant-name',
'Test Merchant',
'--merchant-url',
'https://example.com',
'--context',
VALID_CONTEXT,
'--amount',
'5000',
'--metadata',
'order_id:ord_123,team:growth',
'--no-request-approval',
'--json',
);

expect(result.exitCode).toBe(0);
const sentBody = JSON.parse(lastRequest.body);
expect(sentBody.metadata).toEqual({
order_id: 'ord_123',
team: 'growth',
});
});

it('does not include metadata in POST body when --metadata is omitted', async () => {
setNextResponse(200, BASE_REQUEST);

const result = await runProdCli(
'spend-request',
'create',
'--payment-method-id',
'pd_prod_test',
'--merchant-name',
'Test Merchant',
'--merchant-url',
'https://example.com',
'--context',
VALID_CONTEXT,
'--amount',
'5000',
'--no-request-approval',
'--json',
);

expect(result.exitCode).toBe(0);
const sentBody = JSON.parse(lastRequest.body);
expect(sentBody.metadata).toBeUndefined();
});

it('sends test flag in POST body when --test is used', async () => {
setNextResponse(200, BASE_REQUEST);

Expand Down Expand Up @@ -808,6 +863,27 @@ describe('production mode', () => {
expect(card.number).toBe('4000009990001984');
});

it('returns metadata in JSON output when present', async () => {
setNextResponse(200, {
...BASE_REQUEST,
metadata: { order_id: 'ord_123', team: 'growth' },
});

const result = await runProdCli(
'spend-request',
'retrieve',
'lsrq_prod_001',
'--json',
);

expect(result.exitCode).toBe(0);
const output = parseJson(result.stdout) as Record<string, unknown>[];
expect(output[0].metadata).toEqual({
order_id: 'ord_123',
team: 'growth',
});
});

it('returns card with billing_address and valid_until when present', async () => {
setNextResponse(200, {
...BASE_REQUEST,
Expand Down
24 changes: 24 additions & 0 deletions packages/sdk/src/resources/__tests__/spend-request.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,16 @@ describe('SpendRequestResource', () => {
});
});

it('does not include metadata in POST body when not set', async () => {
mockFetchResponse(200, spendRequestResponse);

await repo.createSpendRequest(validParams);

const [, opts] = mockFetch.mock.calls[0];
const sentBody = JSON.parse(opts.body);
expect(sentBody.metadata).toBeUndefined();
});

it('serializes test flag in POST body when true', async () => {
const paramsWithTest: CreateSpendRequestParams = {
...validParams,
Expand Down Expand Up @@ -397,6 +407,20 @@ describe('SpendRequestResource', () => {
});
});

it('returns metadata when present in the response', async () => {
mockFetchResponse(200, {
...spendRequestResponse,
metadata: { order_id: 'ord_123', team: 'growth' },
});

const result = await repo.getSpendRequest('si_123');

expect(result?.metadata).toEqual({
order_id: 'ord_123',
team: 'growth',
});
});

it('normalizes legacy string shared_payment_token to object form', async () => {
mockFetchResponse(200, {
...spendRequestResponse,
Expand Down
1 change: 1 addition & 0 deletions packages/sdk/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export interface SpendRequest {
payment_status_details?: PaymentStatusDetails | null;
link_transaction_id?: string;
activity_url?: string;
metadata?: Record<string, string>;
created_at: string;
updated_at: string;
}
Expand Down
2 changes: 1 addition & 1 deletion plugins/link/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "link",
"version": "0.10.1",
"version": "0.11.0",
"description": "Authenticate with Link, create spend requests, and retrieve one-time-use card or shared payment token credentials for user-approved purchases.",
"author": {
"name": "Stripe"
Expand Down
2 changes: 1 addition & 1 deletion plugins/link/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "link",
"version": "0.10.1",
"version": "0.11.0",
"description": "Secure, one-time-use payment credentials from Link",
"author": {
"name": "Stripe",
Expand Down
2 changes: 1 addition & 1 deletion plugins/link/.cursor-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "link",
"displayName": "Stripe Link",
"version": "0.10.1",
"version": "0.11.0",
"description": "Get secure, one-time-use payment credentials from a Link wallet so agents can complete purchases on your behalf.",
"author": {
"name": "Stripe"
Expand Down
2 changes: 1 addition & 1 deletion skills/create-payment-credential/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
version: 0.10.1
version: 0.11.0
name: create-payment-credential
description: |
Gets secure, one-time-use payment credentials (cards, tokens) from a Link wallet so agents can complete purchases on behalf of users. Use when the user says "get me a card", "buy something", "pay for X", "make a purchase", "I need to pay", "complete checkout", or asks to transact on any merchant site. Use when the user asks to connect or log in to or sign up for their Link account.
Expand Down
Loading