npm package: @holdmysub/react
Description: React components for HoldMySub paywall integration - setup in 30 seconds
Author: HoldMySub
Latest version: 1.0.2

# @holdmysub/react

React components for HoldMySub paywall integration. Setup in 30 seconds! 🚀

[![npm version](https://badge.fury.io/js/%40holdmysub%2Freact.svg)](https://www.npmjs.com/package/@holdmysub/react)

## Quick Start

### Automatic Setup (Recommended)

```bash
npx create-holdmysub-app
```

This will:
- ✅ Install the package
- ✅ Add the script to your layout
- ✅ Create example components
- ✅ Generate documentation

### Manual Setup

1. **Install the package:**
```bash
npm install @holdmysub/react
```

2. **Add the script to your layout:**

**Next.js App Router** (`app/layout.tsx`):
```tsx
import Script from 'next/script';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        {children}
        <Script src="https://holdmysub.com/embed.js" strategy="afterInteractive" />
      </body>
    </html>
  );
}
```

**Next.js Pages Router** (`pages/_app.tsx`):
```tsx
import Script from 'next/script';

export default function App({ Component, pageProps }) {
  return (
    <>
      <Component {...pageProps} />
      <Script src="https://holdmysub.com/embed.js" strategy="afterInteractive" />
    </>
  );
}
```

**Other React frameworks:** Add `<script src="https://holdmysub.com/embed.js"></script>` to your HTML.

## Usage

### PaywallButton

The main component for adding payment buttons:

```tsx
import { PaywallButton } from '@holdmysub/react';

function PremiumContent() {
  return (
    <PaywallButton
      appId="your-app-id"
      productId="your-product-id"
      customerEmail={user.email}
      onSuccess={(paymentInfo) => {
        console.log('Payment successful!', paymentInfo);
        // Redirect or refresh content
      }}
      onCancel={() => console.log('Payment cancelled')}
      variant="primary"
      size="lg"
    >
      Unlock Premium - $9.99
    </PaywallButton>
  );
}
```

### PaywallGate

Conditionally show content based on payment:

```tsx
import { PaywallGate } from '@holdmysub/react';

function ProtectedContent() {
  return (
    <PaywallGate
      appId="your-app-id"
      productId="your-product-id"
      customerEmail={user.email}
      fallback={
        <div>Please pay to access this content</div>
      }
    >
      <div>🎉 Premium content here!</div>
    </PaywallGate>
  );
}
```

### usePaywall Hook

For advanced use cases:

```tsx
import { usePaywall } from '@holdmysub/react';

function CustomPayment() {
  const { createCheckout, loading, error } = usePaywall(
    'your-app-id',
    'your-product-id',
    user.email
  );

  const handlePayment = async () => {
    try {
      const checkoutUrl = await createCheckout();
      window.location.href = checkoutUrl;
    } catch (err) {
      console.error('Payment failed:', err);
    }
  };

  return (
    <button onClick={handlePayment} disabled={loading}>
      {loading ? 'Creating checkout...' : 'Pay Now'}
    </button>
  );
}
```

## Server-side Payment Verification

Verify payments in API routes or server components:

```tsx
import { verifyPayment, requirePayment } from '@holdmysub/react';

// API route example
export async function GET(request) {
  const email = request.headers.get('user-email');

  // Check if user has paid
  const paymentStatus = await verifyPayment('your-product-id', email);

  if (!paymentStatus.hasPaid) {
    return Response.json({ error: 'Payment required' }, { status: 402 });
  }

  return Response.json({ data: 'premium-content' });
}

// Or use requirePayment to throw on unpaid
export async function POST(request) {
  const email = request.headers.get('user-email');

  // Throws error if not paid
  await requirePayment('your-product-id', email);

  // User has paid, continue...
  return Response.json({ success: true });
}
```

## Environment Variables

Add to your `.env.local`:

```env
HOLDMYSUB_API_KEY=your-api-key
HOLDMYSUB_BASE_URL=https://holdmysub.com  # optional
```

## Component Props

### PaywallButton Props

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `appId` | string | ✅ | Your HoldMySub App ID |
| `productId` | string | ✅ | Product ID to sell |
| `customerEmail` | string | ✅ | Customer's email |
| `onSuccess` | function | ❌ | Called when payment succeeds |
| `onCancel` | function | ❌ | Called when payment cancelled |
| `onError` | function | ❌ | Called on payment errors |
| `successUrl` | string | ❌ | Custom success redirect URL |
| `cancelUrl` | string | ❌ | Custom cancel redirect URL |
| `children` | ReactNode | ❌ | Button content |
| `buttonText` | string | ❌ | Button text (if no children) |
| `variant` | 'primary' \| 'secondary' \| 'success' \| 'danger' | ❌ | Button style variant |
| `size` | 'sm' \| 'md' \| 'lg' | ❌ | Button size |
| `className` | string | ❌ | Additional CSS classes |
| `style` | CSSProperties | ❌ | Inline styles |
| `disabled` | boolean | ❌ | Disable the button |

## TypeScript Support

This package includes full TypeScript definitions:

```tsx
import type { PaymentSuccessInfo, PaywallButtonProps } from '@holdmysub/react';

const handleSuccess = (paymentInfo: PaymentSuccessInfo) => {
  // paymentInfo is fully typed
  console.log(paymentInfo.amount, paymentInfo.currency);
};
```

## Examples

### Basic Usage

```tsx
<PaywallButton
  appId="app_123"
  productId="prod_456"
  customerEmail="user@example.com"
>
  Buy Now - $19.99
</PaywallButton>
```

### With Styling

```tsx
<PaywallButton
  appId="app_123"
  productId="prod_456"
  customerEmail="user@example.com"
  className="w-full bg-purple-600 hover:bg-purple-700"
  size="lg"
>
  🚀 Unlock Premium
</PaywallButton>
```

### With Callbacks

```tsx
<PaywallButton
  appId="app_123"
  productId="prod_456"
  customerEmail="user@example.com"
  onSuccess={(payment) => {
    // Show success message
    toast.success(`Payment of ${payment.formattedAmount} successful!`);
    // Refresh the page to show new content
    router.refresh();
  }}
  onCancel={() => {
    toast.info('Payment cancelled');
  }}
  onError={(error) => {
    toast.error(`Payment failed: ${error.message}`);
  }}
>
  Subscribe Now
</PaywallButton>
```

## Support

- 📚 [Documentation](https://holdmysub.com/docs)
- 💬 [Discord Community](https://discord.gg/holdmysub)
- 📧 [Email Support](mailto:support@holdmysub.com)
- 🐛 [Report Issues](https://github.com/holdmysub/packages/issues)

## License

MIT © [HoldMySub](https://holdmysub.com)