Fetch Payment Link

The Fetch Payment Link endpoint retrieves detailed information about a specific payment link using its unique identifier. This endpoint provides complete payment link data including configuration settings, product associations, payment options, and current status.

Endpoint DetailsCopied!

  • Method: GET

  • URL: /api/v0/payment-links/{id}

  • Content-Type: application/json

  • Authentication: Required (x-client-key and x-client-secret)

  • Scope Required: payment_link:read

Request HeadersCopied!

Header

Type

Required

Description

x-client-key

string

Yes

Your API client key

x-client-secret

string

Yes

Your API client secret

Path ParametersCopied!

Parameter

Type

Required

Description

id

string

Yes

The unique UUID of the payment link to retrieve

ResponseCopied!

Success Response (200 OK)

Returns a complete payment link object with all configuration details:

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "title": "Premium Subscription",
  "description": "Monthly access to all premium features with priority support",
  "url": "premium-subscription",
  "linkType": "PRODUCT",
  "status": "ACTIVE",
  "amount": 29.99,
  "currency": "usdc",
  "recurringType": "MONTHLY",
  "walletId": "wallet_550e8400-e29b-41d4-a716-446655440000",
  "appId": "app_550e8400-e29b-41d4-a716-446655440000",
  "createdAt": "2024-01-15T10:30:00Z",
  "expiration_date": "2024-12-31T23:59:59Z",
  "coverImage": "https://example.com/images/premium-subscription.jpg",
  "paymentForId": "sub_premium_2024",
  "customerId": null,
  "taxId": "tax_550e8400-e29b-41d4-a716-446655440000",
  "allowQuantityAdjustment": true,
  "collectTax": true,
  "collectAddress": true,
  "requirePhoneNumber": true,
  "limitPayments": true,
  "maxPayments": 1000,
  "allowBusinessTaxId": false,
  "allowMobilePayment": true,
  "allowCryptoPayment": true,
  "confirmationPage": "SHOW",
  "createInvoicePdf": true,
  "isForAllProduct": false,
  "customFields": {
    "company_size": {
      "type": "select",
      "required": true,
      "options": ["1-10", "11-50", "51-200", "200+"]
    },
    "use_case": {
      "type": "textarea",
      "required": false,
      "placeholder": "How will you use our service?"
    }
  }
}

Product Bundle Payment Link Response

{
  "id": "550e8400-e29b-41d4-a716-446655440001",
  "title": "Startup Bundle",
  "description": "Everything you need to get started",
  "url": "startup-bundle",
  "linkType": "PRODUCT",
  "status": "ACTIVE",
  "amount": null,
  "currency": "usdc",
  "recurringType": "ONE_TIME",
  "walletId": "wallet_550e8400-e29b-41d4-a716-446655440000",
  "appId": "app_550e8400-e29b-41d4-a716-446655440000",
  "createdAt": "2024-01-10T08:15:00Z",
  "expiration_date": null,
  "coverImage": null,
  "paymentForId": "bundle_startup_2024",
  "customerId": null,
  "taxId": "tax_550e8400-e29b-41d4-a716-446655440000",
  "allowQuantityAdjustment": true,
  "collectTax": true,
  "collectAddress": true,
  "requirePhoneNumber": false,
  "limitPayments": false,
  "maxPayments": null,
  "allowBusinessTaxId": true,
  "allowMobilePayment": false,
  "allowCryptoPayment": true,
  "confirmationPage": "SHOW",
  "createInvoicePdf": false,
  "isForAllProduct": false,
  "customFields": null,
  "paymentLinkProducts": [
    {
      "productId": "123e4567-e89b-12d3-a456-426614174003",
      "quantity": 1
    },
    {
      "productId": "123e4567-e89b-12d3-a456-426614174004",
      "quantity": 2
    }
  ]
}

Error Responses

404 Not Found - Payment Link Not Found
{
  "statusCode": 404,
  "message": "Payment link not found",
  "error": "Not Found"
}
401 Unauthorized - Invalid Authentication
{
  "statusCode": 401,
  "message": "Application not authenticated",
  "error": "Unauthorized"
}
401 Unauthorized - Access Denied
{
  "statusCode": 401,
  "message": "Access denied to this payment link",
  "error": "Unauthorized"
}
403 Forbidden - Insufficient Scope
{
  "statusCode": 403,
  "message": "Insufficient scope for this operation",
  "error": "Forbidden"
}
400 Bad Request - Invalid UUID Format
{
  "statusCode": 400,
  "message": "Invalid UUID format",
  "error": "Bad Request"
}
429 Too Many Requests
{
  "statusCode": 429,
  "message": "Rate limit exceeded",
  "error": "Too Many Requests"
}

Example RequestsCopied!

Basic Payment Link Fetch

curl -X GET "https://api.devdraft.com/api/v0/payment-links/550e8400-e29b-41d4-a716-446655440000" \
  -H "x-client-key: your_client_key" \
  -H "x-client-secret: your_client_secret"

JavaScript/TypeScript Example

async function fetchPaymentLink(paymentLinkId) {
  try {
    const response = await fetch(`/api/v0/payment-links/${paymentLinkId}`, {
      method: 'GET',
      headers: {
        'x-client-key': 'your_client_key',
        'x-client-secret': 'your_client_secret'
      }
    });

    if (response.ok) {
      const paymentLink = await response.json();
      console.log('Payment link retrieved:', paymentLink);
      return paymentLink;
    } else if (response.status === 404) {
      console.log('Payment link not found');
      return null;
    } else if (response.status === 401) {
      console.log('Access denied or authentication failed');
      throw new Error('Unauthorized access');
    } else {
      console.error('Failed to fetch payment link:', response.statusText);
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }
  } catch (error) {
    console.error('Request failed:', error);
    throw error;
  }
}

// Usage
const paymentLink = await fetchPaymentLink('550e8400-e29b-41d4-a716-446655440000');

Python Example

import requests

def fetch_payment_link(payment_link_id, client_key, client_secret):
    """Fetch a payment link by ID from the Devdraft API"""
    
    url = f"https://api.devdraft.com/api/v0/payment-links/{payment_link_id}"
    headers = {
        'x-client-key': client_key,
        'x-client-secret': client_secret
    }
    
    try:
        response = requests.get(url, headers=headers)
        
        if response.status_code == 200:
            return response.json()
        elif response.status_code == 404:
            print(f"Payment link {payment_link_id} not found")
            return None
        elif response.status_code == 401:
            print("Access denied or authentication failed")
            raise ValueError("Unauthorized access")
        else:
            response.raise_for_status()
            
    except requests.exceptions.RequestException as e:
        print(f"Error fetching payment link: {e}")
        raise

# Usage
payment_link = fetch_payment_link(
    '550e8400-e29b-41d4-a716-446655440000',
    'your_client_key',
    'your_client_secret'
)

Response FieldsCopied!

Core Information

Field

Type

Description

id

string

Unique payment link identifier (UUID)

title

string

Display title of the payment link

description

string

Detailed description (nullable)

url

string

URL slug for accessing the payment link

status

enum

Current status (ACTIVE, DEACTIVATED)

createdAt

datetime

Payment link creation timestamp

Payment Configuration

Field

Type

Description

linkType

enum

Type of payment link (PRODUCT, DONATION, SUBSCRIPTION, etc.)

amount

number

Fixed amount (nullable for product bundles)

currency

enum

Payment currency (usdc, eurc)

recurringType

enum

Payment frequency (ONE_TIME, MONTHLY, etc.)

expiration_date

datetime

Link expiration date (nullable)

Associated Resources

Field

Type

Description

walletId

string

Associated wallet identifier

appId

string

Your application identifier

customerId

string

Specific customer restriction (nullable)

taxId

string

Tax configuration ID (nullable)

paymentForId

string

External reference identifier (nullable)

coverImage

string

Cover image URL (nullable)

Customer Experience Settings

Field

Type

Description

allowQuantityAdjustment

boolean

Can customers adjust product quantities

collectTax

boolean

Tax collection enabled

collectAddress

boolean

Customer address collection required

requirePhoneNumber

boolean

Phone number collection required

allowBusinessTaxId

boolean

Business tax ID collection enabled

allowMobilePayment

boolean

Mobile payment options available

allowCryptoPayment

boolean

Cryptocurrency payments enabled

Advanced Configuration

Field

Type

Description

limitPayments

boolean

Payment count is limited

maxPayments

number

Maximum number of payments allowed (nullable)

isForAllProduct

boolean

Includes all products from catalog

confirmationPage

enum

Post-payment page behavior (SHOW, REDIRECT)

createInvoicePdf

boolean

Generate PDF receipts

customFields

object

Custom form fields configuration (nullable)

Product Association

Field

Type

Description

paymentLinkProducts

array

Associated products with quantities (for bundles)

paymentLinkProducts[].productId

string

Product UUID

paymentLinkProducts[].quantity

number

Product quantity

Integration PatternsCopied!

Payment Link Details Display

function PaymentLinkDetails({ paymentLinkId }) {
  const [paymentLink, setPaymentLink] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function loadPaymentLink() {
      try {
        setLoading(true);
        const data = await fetchPaymentLink(paymentLinkId);
        setPaymentLink(data);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    }

    if (paymentLinkId) {
      loadPaymentLink();
    }
  }, [paymentLinkId]);

  if (loading) return <div>Loading payment link...</div>;
  if (error) return <div>Error: {error}</div>;
  if (!paymentLink) return <div>Payment link not found</div>;

  const isExpired = paymentLink.expiration_date && 
    new Date(paymentLink.expiration_date) < new Date();

  return (
    <div className="payment-link-details">
      <div className="header">
        <h2>{paymentLink.title}</h2>
        <span className={`status ${paymentLink.status.toLowerCase()}`}>
          {paymentLink.status}
        </span>
        {isExpired && <span className="expired">EXPIRED</span>}
      </div>
      
      {paymentLink.coverImage && (
        <img 
          src={paymentLink.coverImage} 
          alt={paymentLink.title}
          className="cover-image"
        />
      )}
      
      <div className="description">
        <p>{paymentLink.description}</p>
      </div>
      
      <div className="payment-info">
        <div className="amount">
          {paymentLink.amount ? (
            <span>${paymentLink.amount} {paymentLink.currency.toUpperCase()}</span>
          ) : (
            <span>Variable Amount</span>
          )}
        </div>
        <div className="frequency">
          {paymentLink.recurringType !== 'ONE_TIME' && (
            <span>Recurring: {paymentLink.recurringType}</span>
          )}
        </div>
      </div>

      <div className="configuration">
        <h3>Configuration</h3>
        <ul>
          <li>Type: {paymentLink.linkType}</li>
          <li>Tax Collection: {paymentLink.collectTax ? 'Enabled' : 'Disabled'}</li>
          <li>Address Required: {paymentLink.collectAddress ? 'Yes' : 'No'}</li>
          <li>Phone Required: {paymentLink.requirePhoneNumber ? 'Yes' : 'No'}</li>
          {paymentLink.limitPayments && (
            <li>Payment Limit: {paymentLink.maxPayments}</li>
          )}
        </ul>
      </div>

      {paymentLink.paymentLinkProducts && (
        <div className="products">
          <h3>Products ({paymentLink.paymentLinkProducts.length})</h3>
          <ul>
            {paymentLink.paymentLinkProducts.map((product, index) => (
              <li key={index}>
                Product ID: {product.productId} (Qty: {product.quantity})
              </li>
            ))}
          </ul>
        </div>
      )}

      <div className="payment-methods">
        <h3>Accepted Payment Methods</h3>
        <ul>
          {paymentLink.allowCryptoPayment && <li>Cryptocurrency</li>}
          {paymentLink.allowMobilePayment && <li>Mobile Payment</li>}
          <li>Credit/Debit Card</li>
        </ul>
      </div>
      
      <div className="meta">
        <p><strong>Created:</strong> {new Date(paymentLink.createdAt).toLocaleDateString()}</p>
        {paymentLink.expiration_date && (
          <p><strong>Expires:</strong> {new Date(paymentLink.expiration_date).toLocaleDateString()}</p>
        )}
        <p><strong>URL:</strong> <code>{paymentLink.url}</code></p>
      </div>
    </div>
  );
}

Payment Link Validation

async function validatePaymentLink(paymentLinkId) {
  try {
    const paymentLink = await fetchPaymentLink(paymentLinkId);
    
    if (!paymentLink) {
      return { valid: false, reason: 'Payment link not found' };
    }

    if (paymentLink.status !== 'ACTIVE') {
      return { valid: false, reason: 'Payment link is not active' };
    }

    const now = new Date();
    if (paymentLink.expiration_date && new Date(paymentLink.expiration_date) < now) {
      return { valid: false, reason: 'Payment link has expired' };
    }

    if (paymentLink.limitPayments && paymentLink.maxPayments <= 0) {
      return { valid: false, reason: 'Payment limit reached' };
    }

    return { 
      valid: true, 
      paymentLink: paymentLink,
      requiresAddress: paymentLink.collectAddress,
      requiresPhone: paymentLink.requirePhoneNumber,
      allowsQuantityAdjustment: paymentLink.allowQuantityAdjustment
    };
  } catch (error) {
    return { 
      valid: false, 
      reason: 'Failed to validate payment link',
      error: error.message 
    };
  }
}

// Usage in checkout flow
async function initializeCheckout(paymentLinkId) {
  const validation = await validatePaymentLink(paymentLinkId);
  
  if (!validation.valid) {
    throw new Error(`Cannot proceed with checkout: ${validation.reason}`);
  }
  
  // Configure checkout form based on payment link settings
  const checkoutConfig = {
    paymentLink: validation.paymentLink,
    formFields: {
      address: validation.requiresAddress,
      phone: validation.requiresPhone
    },
    features: {
      quantityAdjustment: validation.allowsQuantityAdjustment,
      customFields: validation.paymentLink.customFields
    }
  };
  
  return checkoutConfig;
}

Cache Management

class PaymentLinkCache {
  constructor() {
    this.cache = new Map();
    this.cacheTimeout = 10 * 60 * 1000; // 10 minutes
  }
  
  async getPaymentLink(id) {
    const cached = this.cache.get(id);
    
    if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
      return cached.data;
    }
    
    const paymentLink = await fetchPaymentLink(id);
    
    if (paymentLink) {
      this.cache.set(id, {
        data: paymentLink,
        timestamp: Date.now()
      });
    }
    
    return paymentLink;
  }
  
  invalidate(id) {
    this.cache.delete(id);
  }
  
  clear() {
    this.cache.clear();
  }
}

const paymentLinkCache = new PaymentLinkCache();

// Usage
const paymentLink = await paymentLinkCache.getPaymentLink(paymentLinkId);

Security FeaturesCopied!

  • App Isolation: Can only access payment links belonging to your application

  • Access Control: Validates ownership before returning data

  • Scope Validation: Requires payment_link:read scope

  • Authentication Required: All requests must include valid API credentials

Rate LimitingCopied!

  • Limit: 100 requests per minute per API key

  • Headers: Monitor X-RateLimit-* headers in responses

  • Best Practice: Implement caching to reduce API calls

Common Use CasesCopied!

Checkout Integration

Retrieve payment link details to configure checkout forms and validation rules.

Payment Link Management

Display payment link information in admin dashboards for monitoring and management.

Validation Before Processing

Verify payment link status and configuration before processing payments.

Analytics and Reporting

Fetch payment link data for usage analysis and performance reporting.

Dynamic Form Generation

Use payment link configuration to dynamically generate appropriate checkout forms.