CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-vkontakte--vk-bridge

Bridge library for VK Mini Apps to communicate with VK clients across iOS, Android, and Web platforms

Pending
Overview
Eval results
Files

payments-commerce.mddocs/

Payments & Commerce

Payment processing and e-commerce functionality for monetizing VK Mini Apps through VK Pay integration.

Capabilities

Payment Processing

Process payments through VK Pay system with support for various payment methods and transaction types.

/**
 * Open VK Pay payment form
 * @param props - Payment configuration with action and parameters
 */
function send(method: 'VKWebAppOpenPayForm', props: VKPayProps<VKPayActionType>): Promise<
  TransactionResult | { result: TransactionResult }
>;

interface VKPayProps<T extends VKPayActionType> {
  /** VK application ID */
  app_id: number;
  /** Payment action type */
  action: T;
  /** Action-specific parameters */
  params: VKPayActionParamsMap[T];
}

type VKPayActionType = 'pay-to-service' | 'pay-to-user' | 'pay-to-group';

interface VKPayActionParamsMap {
  'pay-to-service': {
    /** Merchant ID */
    merchant_id: number;
    /** Payment amount */
    amount: number;
    /** Payment description */
    description: string;
    /** Unique transaction ID */
    order_id: string;
    /** Additional data */
    data?: string;
  };
  'pay-to-user': {
    /** Recipient user ID */
    user_id: number;
    /** Payment amount */
    amount: number;
    /** Payment description */
    description: string;
  };
  'pay-to-group': {
    /** Recipient group ID */
    group_id: number;
    /** Payment amount */
    amount: number;
    /** Payment description */
    description: string;
  };
}

interface TransactionResult {
  /** Transaction status */
  status: 'success' | 'cancel' | 'fail';
  /** Transaction ID */
  transaction_id?: string;
  /** Payment amount */
  amount?: number;
  /** Additional transaction data */
  extra?: any;
}

Usage Examples:

// Service payment (in-app purchase)
const servicePayment = await bridge.send('VKWebAppOpenPayForm', {
  app_id: 51665960,
  action: 'pay-to-service',
  params: {
    merchant_id: 12345,
    amount: 100, // Amount in kopecks (1 ruble = 100 kopecks)
    description: 'Premium features unlock',
    order_id: `order_${Date.now()}`,
    data: JSON.stringify({
      feature: 'premium',
      duration: '1month'
    })
  }
});

if (servicePayment.status === 'success') {
  console.log('Payment successful:', servicePayment.transaction_id);
  // Unlock premium features
  await unlockPremiumFeatures();
} else if (servicePayment.status === 'cancel') {
  console.log('Payment cancelled by user');
} else {
  console.log('Payment failed');
}

// User-to-user payment
const userPayment = await bridge.send('VKWebAppOpenPayForm', {
  app_id: 51665960,
  action: 'pay-to-user',
  params: {
    user_id: 12345,
    amount: 500, // 5 rubles
    description: 'Gift from friend'
  }
});

// Group payment (donation)
const groupPayment = await bridge.send('VKWebAppOpenPayForm', {
  app_id: 51665960,
  action: 'pay-to-group',
  params: {
    group_id: 123456789,
    amount: 1000, // 10 rubles
    description: 'Support our community'
  }
});

Payment Flow Patterns

In-App Purchases

interface Product {
  id: string;
  name: string;
  description: string;
  price: number; // in kopecks
  category: 'consumable' | 'non-consumable' | 'subscription';
}

class PaymentManager {
  private appId: number;
  private merchantId: number;

  constructor(appId: number, merchantId: number) {
    this.appId = appId;
    this.merchantId = merchantId;
  }

  async purchaseProduct(product: Product, userId: number): Promise<boolean> {
    try {
      const result = await bridge.send('VKWebAppOpenPayForm', {
        app_id: this.appId,
        action: 'pay-to-service',
        params: {
          merchant_id: this.merchantId,
          amount: product.price,
          description: product.description,
          order_id: `${product.id}_${userId}_${Date.now()}`,
          data: JSON.stringify({
            product_id: product.id,
            user_id: userId,
            category: product.category
          })
        }
      });

      if (result.status === 'success') {
        // Record successful purchase
        await this.recordPurchase(product, userId, result.transaction_id!);
        return true;
      }

      return false;
    } catch (error) {
      console.error('Purchase failed:', error);
      return false;
    }
  }

  private async recordPurchase(product: Product, userId: number, transactionId: string): Promise<void> {
    // Send purchase info to your server for validation and fulfillment
    await fetch('/api/purchases', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        product_id: product.id,
        user_id: userId,
        transaction_id: transactionId,
        amount: product.price,
        timestamp: Date.now()
      })
    });
  }
}

// Usage
const paymentManager = new PaymentManager(51665960, 12345);

const premiumProduct: Product = {
  id: 'premium_month',
  name: 'Premium Subscription',
  description: '1 month premium access',
  price: 29900, // 299 rubles
  category: 'subscription'
};

const success = await paymentManager.purchaseProduct(premiumProduct, currentUserId);
if (success) {
  console.log('Premium subscription activated!');
}

Error Handling

async function handlePayment(paymentParams: VKPayProps<VKPayActionType>) {
  try {
    const result = await bridge.send('VKWebAppOpenPayForm', paymentParams);
    
    switch (result.status) {
      case 'success':
        console.log('Payment successful');
        // Process successful payment
        await processSuccessfulPayment(result);
        break;
        
      case 'cancel':
        console.log('Payment cancelled by user');
        // Handle cancellation (maybe show retry option)
        showPaymentCancelledMessage();
        break;
        
      case 'fail':
        console.log('Payment failed');
        // Handle payment failure
        showPaymentFailedMessage();
        break;
    }
    
    return result;
  } catch (error) {
    console.error('Payment error:', error);
    
    if (error.error_type === 'client_error') {
      switch (error.error_data.error_code) {
        case 1:
          console.log('Payment method not supported');
          break;
        case 2:
          console.log('Insufficient funds');
          break;
        default:
          console.log('Payment client error:', error.error_data.error_reason);
      }
    }
    
    throw error;
  }
}

Security Considerations

// Always validate payments on your server
async function validatePayment(transactionId: string, expectedAmount: number): Promise<boolean> {
  try {
    const response = await fetch('/api/validate-payment', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        transaction_id: transactionId,
        expected_amount: expectedAmount
      })
    });
    
    const validation = await response.json();
    return validation.valid === true;
  } catch (error) {
    console.error('Payment validation failed:', error);
    return false;
  }
}

// Use validation in payment flow
const paymentResult = await bridge.send('VKWebAppOpenPayForm', paymentParams);

if (paymentResult.status === 'success') {
  const isValid = await validatePayment(
    paymentResult.transaction_id!,
    paymentParams.params.amount
  );
  
  if (isValid) {
    // Safe to fulfill the purchase
    await fulfillPurchase(paymentResult);
  } else {
    console.error('Payment validation failed');
    // Handle invalid payment
  }
}

Install with Tessl CLI

npx tessl i tessl/npm-vkontakte--vk-bridge

docs

advertising-monetization.md

application-lifecycle.md

authentication.md

core-bridge.md

device-features.md

geolocation.md

index.md

launch-parameters.md

middleware.md

payments-commerce.md

qr-barcode-scanning.md

social-features.md

storage-data.md

ui-display.md

user-data.md

tile.json