mirror of
https://github.com/xtr-dev/payload-billing.git
synced 2025-12-10 02:43:24 +00:00
Compare commits
16 Commits
v0.1.4
...
claude/iss
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
38c8c3677d | ||
|
|
e74a2410e6 | ||
|
|
27b86132e9 | ||
| ec635fb707 | |||
| cabe6eda96 | |||
| a3108a0f49 | |||
|
|
113a0d36c0 | ||
| 8ac328e14f | |||
| 7a3d6ec26e | |||
| 534b0e440f | |||
|
|
669a9decd5 | ||
| bfa214aed6 | |||
| c083ae183c | |||
| d09fe3054a | |||
|
|
50ab001e94 | ||
| 29db6635b8 |
235
.github/CLAUDE_PR_ASSISTANT.md
vendored
Normal file
235
.github/CLAUDE_PR_ASSISTANT.md
vendored
Normal file
@@ -0,0 +1,235 @@
|
||||
# Claude PR Assistant
|
||||
|
||||
This workflow allows Claude to assist with Pull Requests by implementing improvements, fixes, and changes directly on the PR branch when mentioned in comments.
|
||||
|
||||
## How to Use
|
||||
|
||||
### 1. Comment on Any PR
|
||||
|
||||
Use one of these trigger commands in a comment on any open pull request:
|
||||
|
||||
- `@claude implement` - Implement new features or functionality
|
||||
- `@claude fix` - Fix bugs or issues in the PR
|
||||
- `@claude improve` - Improve existing code quality
|
||||
- `@claude update` - Update code to match requirements
|
||||
- `@claude refactor` - Refactor code for better structure
|
||||
- `@claude help` - General assistance with the PR
|
||||
|
||||
### 2. Example Usage
|
||||
|
||||
```
|
||||
@claude fix
|
||||
|
||||
Please fix the TypeScript errors in the payments collection and ensure proper typing for the provider data field.
|
||||
```
|
||||
|
||||
```
|
||||
@claude improve
|
||||
|
||||
Can you optimize the invoice generation logic and add better error handling?
|
||||
```
|
||||
|
||||
```
|
||||
@claude implement
|
||||
|
||||
Add validation for email addresses in the customer billing information fields. Make sure it follows the existing validation patterns.
|
||||
```
|
||||
|
||||
## What Happens
|
||||
|
||||
1. **Permission Check**: Verifies you are `bvdaakster` (only authorized user)
|
||||
2. **PR Analysis**: Claude analyzes the current PR context and changes
|
||||
3. **Implementation**: Makes the requested changes directly on the PR branch
|
||||
4. **Quality Checks**: Runs build, typecheck, and lint
|
||||
5. **Commit & Push**: Commits changes with descriptive messages
|
||||
6. **Notification**: Updates the PR with completion status
|
||||
|
||||
## Features
|
||||
|
||||
### ✅ Direct PR Modification
|
||||
- Works directly on the existing PR branch
|
||||
- No new branches or PRs created
|
||||
- Changes appear immediately in the PR
|
||||
|
||||
### ✅ Smart Context Awareness
|
||||
- Understands the current PR changes
|
||||
- Analyzes existing code patterns
|
||||
- Maintains consistency with the codebase
|
||||
|
||||
### ✅ Comprehensive Request Handling
|
||||
- Code improvements and refactoring
|
||||
- Bug fixes within the PR
|
||||
- Adding missing features
|
||||
- Documentation updates
|
||||
- Type error fixes
|
||||
- Performance optimizations
|
||||
- Test additions
|
||||
|
||||
### ✅ Quality Assurance
|
||||
- Follows TypeScript conventions
|
||||
- Uses ESM module structure
|
||||
- Runs automated quality checks
|
||||
- Maintains coding standards
|
||||
|
||||
## Command Reference
|
||||
|
||||
| Command | Purpose | Example |
|
||||
|---------|---------|---------|
|
||||
| `@claude implement` | Add new functionality | "implement user authentication" |
|
||||
| `@claude fix` | Fix bugs or errors | "fix the validation logic" |
|
||||
| `@claude improve` | Enhance existing code | "improve performance of query" |
|
||||
| `@claude update` | Update to requirements | "update to use new API format" |
|
||||
| `@claude refactor` | Restructure code | "refactor into smaller functions" |
|
||||
| `@claude help` | General assistance | "help with error handling" |
|
||||
|
||||
## Examples
|
||||
|
||||
### Bug Fix Request
|
||||
```
|
||||
@claude fix
|
||||
|
||||
There's a TypeScript error in `src/providers/stripe.ts` on line 45. The `amount` property is missing from the payment object. Please fix this and ensure proper typing.
|
||||
```
|
||||
|
||||
### Feature Implementation
|
||||
```
|
||||
@claude implement
|
||||
|
||||
Add support for recurring payments in the Stripe provider. Follow the same pattern as the Mollie provider and include proper webhook handling.
|
||||
```
|
||||
|
||||
### Code Improvement
|
||||
```
|
||||
@claude improve
|
||||
|
||||
The `validatePayment` function in utils.ts is getting complex. Please refactor it to be more readable and add proper error messages for each validation case.
|
||||
```
|
||||
|
||||
### Documentation Update
|
||||
```
|
||||
@claude update
|
||||
|
||||
Update the JSDoc comments in the billing plugin configuration to include examples of how to use the new customer info extractor feature.
|
||||
```
|
||||
|
||||
## Response Types
|
||||
|
||||
### Success Response
|
||||
```
|
||||
✅ PR Assistance Complete!
|
||||
|
||||
🔧 Changes Made:
|
||||
- ✅ Analyzed PR context and requirements
|
||||
- ✅ Implemented requested improvements/fixes
|
||||
- ✅ Followed project coding standards
|
||||
- ✅ Updated the current PR branch
|
||||
|
||||
Changes are ready for review! 🚀
|
||||
```
|
||||
|
||||
### No Changes Response
|
||||
```
|
||||
ℹ️ PR Assistance Complete - No Changes
|
||||
|
||||
Analysis Result: The requested feature is already implemented
|
||||
|
||||
💡 Suggestions:
|
||||
- Provide more detailed requirements
|
||||
- Point to specific files or functions
|
||||
```
|
||||
|
||||
### Error Response
|
||||
```
|
||||
❌ PR Assistance Failed
|
||||
|
||||
🔄 Try Again:
|
||||
- Providing more specific instructions
|
||||
- Breaking down complex requests
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Clear Instructions
|
||||
- Be specific about what you want changed
|
||||
- Reference specific files or functions when possible
|
||||
- Provide context about the desired outcome
|
||||
|
||||
### Examples of Good Requests
|
||||
```
|
||||
@claude fix the TypeScript error in src/collections/payments.ts line 42 where the status field is missing from the Payment interface
|
||||
|
||||
@claude implement email validation in the customer billing form using the same pattern as the phone validation
|
||||
|
||||
@claude refactor the webhook handler in providers/mollie.ts to extract the payment processing logic into separate functions
|
||||
```
|
||||
|
||||
### Examples of Unclear Requests
|
||||
```
|
||||
@claude fix everything
|
||||
@claude make it better
|
||||
@claude help with this
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
- **User Restriction**: Only `bvdaakster` can use this feature
|
||||
- **PR Only**: Works on pull request comments, not issue comments
|
||||
- **Open PRs**: Only works on open pull requests
|
||||
- **Branch Access**: Requires write access to the PR branch
|
||||
|
||||
## Technical Details
|
||||
|
||||
### Workflow Triggers
|
||||
- Event: `issue_comment` on pull requests
|
||||
- Conditions: Comment contains Claude trigger words
|
||||
- Permissions: User must be `bvdaakster`
|
||||
|
||||
### Quality Checks
|
||||
Claude automatically runs:
|
||||
- `pnpm build` - Verify code compiles
|
||||
- `pnpm typecheck` - Check TypeScript types
|
||||
- `pnpm lint` - Ensure code style compliance
|
||||
- `npm run test` - Run test suite (if available)
|
||||
|
||||
### Commit Messages
|
||||
Automatic commits include:
|
||||
- Description of the request
|
||||
- PR number and requester
|
||||
- Claude attribution
|
||||
- Proper co-authoring
|
||||
|
||||
### Branch Strategy
|
||||
- Works directly on the PR's head branch
|
||||
- No additional branches created
|
||||
- Changes pushed to existing PR
|
||||
- Maintains PR history
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Permission Denied**
|
||||
- Only `bvdaakster` can use Claude PR assistance
|
||||
- Verify you're commenting as the correct user
|
||||
|
||||
2. **No Changes Made**
|
||||
- Request might be unclear or already implemented
|
||||
- Try providing more specific instructions
|
||||
- Reference specific files or lines
|
||||
|
||||
3. **Workflow Failed**
|
||||
- Check the Actions tab for detailed logs
|
||||
- Verify the PR branch is accessible
|
||||
- Ensure request is actionable
|
||||
|
||||
### Getting Help
|
||||
|
||||
If Claude assistance isn't working:
|
||||
1. Check the workflow logs in Actions tab
|
||||
2. Verify your request is specific and actionable
|
||||
3. Try breaking complex requests into smaller parts
|
||||
4. Use different command triggers (@claude fix vs @claude implement)
|
||||
|
||||
---
|
||||
|
||||
**Note**: This feature uses the official Anthropic Claude Code action for reliable, production-ready assistance. All changes should be reviewed before merging.
|
||||
43
.github/claude-config.json
vendored
Normal file
43
.github/claude-config.json
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"privilegedUsers": [
|
||||
"bvdaakster"
|
||||
],
|
||||
"permissions": {
|
||||
"issueImplementation": {
|
||||
"strategy": "privilegedUsers",
|
||||
"description": "Only bvdaakster can request Claude implementations"
|
||||
},
|
||||
"codeReview": {
|
||||
"strategy": "privilegedUsers",
|
||||
"description": "Only bvdaakster can trigger Claude reviews"
|
||||
},
|
||||
"prAssistant": {
|
||||
"strategy": "privilegedUsers",
|
||||
"description": "Only bvdaakster can use Claude PR assistance"
|
||||
}
|
||||
},
|
||||
"workflows": {
|
||||
"issueImplementation": {
|
||||
"file": "claude-implement-issue.yml",
|
||||
"triggers": ["@claude implement", "@claude fix", "@claude create"],
|
||||
"description": "Creates new branch and PR for issue implementation"
|
||||
},
|
||||
"codeReview": {
|
||||
"file": "claude-code-review.yml",
|
||||
"triggers": ["automatic on PR"],
|
||||
"description": "Automatic code review for PRs from privileged users"
|
||||
},
|
||||
"prAssistant": {
|
||||
"file": "claude-pr-assistant.yml",
|
||||
"triggers": ["@claude implement", "@claude fix", "@claude improve", "@claude update", "@claude refactor", "@claude help"],
|
||||
"description": "Assists with PR improvements directly on the PR branch"
|
||||
}
|
||||
},
|
||||
"strategies": {
|
||||
"privilegedUsers": "Only users in the privilegedUsers list",
|
||||
"adminsOnly": "Only repository admins",
|
||||
"adminOrPrivileged": "Repository admins OR users in privilegedUsers list",
|
||||
"orgMembersWithWrite": "Organization members with write access",
|
||||
"everyone": "All users with repository access"
|
||||
}
|
||||
}
|
||||
7
.github/workflows/claude-code-review.yml
vendored
7
.github/workflows/claude-code-review.yml
vendored
@@ -12,11 +12,8 @@ on:
|
||||
|
||||
jobs:
|
||||
claude-review:
|
||||
# Optional: Filter by PR author
|
||||
# if: |
|
||||
# github.event.pull_request.user.login == 'external-contributor' ||
|
||||
# github.event.pull_request.user.login == 'new-developer' ||
|
||||
# github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
|
||||
# Only allow bvdaakster to trigger reviews
|
||||
if: github.event.pull_request.user.login == 'bvdaakster'
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
|
||||
197
.github/workflows/claude-implement-issue.yml
vendored
Normal file
197
.github/workflows/claude-implement-issue.yml
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
name: Claude Issue Implementation
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
claude-implement:
|
||||
if: |
|
||||
github.event.issue_comment.issue.state == 'open' &&
|
||||
(
|
||||
contains(github.event.comment.body, '@claude implement') ||
|
||||
contains(github.event.comment.body, '@claude fix') ||
|
||||
contains(github.event.comment.body, '@claude create')
|
||||
)
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check user permissions
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
if (context.actor !== 'bvdaakster') {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: '❌ **Access Denied**: Only bvdaakster can use Claude implementation.'
|
||||
});
|
||||
throw new Error('Unauthorized user');
|
||||
}
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install pnpm
|
||||
run: npm install -g pnpm@10.12.4
|
||||
|
||||
- name: Create branch
|
||||
id: create-branch
|
||||
run: |
|
||||
BRANCH_NAME="claude/issue-${{ github.event.issue.number }}-$(date +%Y%m%d-%H%M%S)"
|
||||
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
||||
git checkout -b "$BRANCH_NAME"
|
||||
git push origin "$BRANCH_NAME"
|
||||
|
||||
- name: Notify start
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: `🤖 **Claude Implementation Started**\n\n📋 **Issue**: #${{ github.event.issue.number }}\n🌿 **Branch**: \`${{ steps.create-branch.outputs.branch_name }}\`\n\nImplementing your request...`
|
||||
});
|
||||
|
||||
- name: Implement with Claude
|
||||
uses: anthropics/claude-code-action@beta
|
||||
with:
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
direct_prompt: |
|
||||
Please implement the feature or fix described in this GitHub issue:
|
||||
|
||||
**Issue #${{ github.event.issue.number }}**: ${{ github.event.issue.title }}
|
||||
|
||||
**Issue Description**:
|
||||
${{ github.event.issue.body }}
|
||||
|
||||
**User Request**:
|
||||
${{ github.event.comment.body }}
|
||||
|
||||
**Instructions**:
|
||||
1. Analyze the issue requirements carefully
|
||||
2. Follow existing code patterns and conventions
|
||||
3. Use TypeScript with proper typing
|
||||
4. Follow ESM module structure with .js extensions
|
||||
5. Add tests if needed
|
||||
6. Update documentation if necessary
|
||||
|
||||
This is the @xtr-dev/payload-billing plugin for PayloadCMS.
|
||||
allowed_tools: "Bash(pnpm build),Bash(pnpm typecheck),Bash(pnpm lint),Bash(npm run test)"
|
||||
|
||||
- name: Check for changes
|
||||
id: changes
|
||||
run: |
|
||||
if git diff --quiet && git diff --cached --quiet; then
|
||||
echo "has_changes=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "has_changes=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Commit and push
|
||||
if: steps.changes.outputs.has_changes == 'true'
|
||||
run: |
|
||||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "Claude Implementation Bot"
|
||||
git add .
|
||||
git commit -m "feat: implement issue #${{ github.event.issue.number }}
|
||||
|
||||
Implemented via Claude automation.
|
||||
|
||||
Issue: #${{ github.event.issue.number }}
|
||||
Requested by: @${{ github.event.comment.user.login }}
|
||||
|
||||
🤖 Generated with Claude Code
|
||||
|
||||
Co-Authored-By: Claude <noreply@anthropic.com>"
|
||||
git push origin ${{ steps.create-branch.outputs.branch_name }}
|
||||
|
||||
- name: Create PR
|
||||
if: steps.changes.outputs.has_changes == 'true'
|
||||
uses: actions/github-script@v7
|
||||
id: create-pr
|
||||
with:
|
||||
script: |
|
||||
const { data: pr } = await github.rest.pulls.create({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
title: `🤖 Implement: ${{ github.event.issue.title }}`,
|
||||
head: '${{ steps.create-branch.outputs.branch_name }}',
|
||||
base: 'dev',
|
||||
body: `## 🤖 Claude Implementation
|
||||
|
||||
This PR implements issue #${{ github.event.issue.number }}.
|
||||
|
||||
**Issue**: #${{ github.event.issue.number }}
|
||||
**Requested by**: @${{ github.event.comment.user.login }}
|
||||
**Branch**: \`${{ steps.create-branch.outputs.branch_name }}\`
|
||||
|
||||
### Review Checklist
|
||||
- [ ] Code follows project conventions
|
||||
- [ ] Build passes
|
||||
- [ ] Tests pass
|
||||
- [ ] Implementation matches requirements
|
||||
|
||||
Closes #${{ github.event.issue.number }}
|
||||
|
||||
🤖 Generated with Claude Code`
|
||||
});
|
||||
return pr.number;
|
||||
|
||||
- name: Notify success
|
||||
if: steps.changes.outputs.has_changes == 'true'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const prNumber = '${{ steps.create-pr.outputs.result }}';
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: `✅ **Implementation Complete!**\n\n🎯 **Pull Request**: #${prNumber}\n🌿 **Branch**: \`${{ steps.create-branch.outputs.branch_name }}\`\n\nReady for review! 🚀`
|
||||
});
|
||||
|
||||
- name: Handle no changes
|
||||
if: steps.changes.outputs.has_changes == 'false'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: `ℹ️ **No Changes Needed**\n\nI analyzed the issue but no code changes are required.`
|
||||
});
|
||||
|
||||
- name: Clean up on no changes
|
||||
if: steps.changes.outputs.has_changes == 'false'
|
||||
run: git push origin --delete ${{ steps.create-branch.outputs.branch_name }} || true
|
||||
|
||||
- name: Handle failure
|
||||
if: failure()
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: `❌ **Implementation Failed**\n\nCheck the [workflow logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details.`
|
||||
});
|
||||
172
.github/workflows/claude-pr-assistant.yml
vendored
Normal file
172
.github/workflows/claude-pr-assistant.yml
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
name: Claude PR Assistant
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
claude-pr-assist:
|
||||
if: |
|
||||
github.event.issue.pull_request &&
|
||||
github.event.issue_comment.issue.state == 'open' &&
|
||||
(
|
||||
contains(github.event.comment.body, '@claude implement') ||
|
||||
contains(github.event.comment.body, '@claude fix') ||
|
||||
contains(github.event.comment.body, '@claude improve') ||
|
||||
contains(github.event.comment.body, '@claude update') ||
|
||||
contains(github.event.comment.body, '@claude refactor') ||
|
||||
contains(github.event.comment.body, '@claude help')
|
||||
)
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check user permissions
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
if (context.actor !== 'bvdaakster') {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: '❌ **Access Denied**: Only bvdaakster can use Claude PR assistance.'
|
||||
});
|
||||
throw new Error('Unauthorized user');
|
||||
}
|
||||
|
||||
- name: Get PR details
|
||||
id: pr-details
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { data: pr } = await github.rest.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.issue.number
|
||||
});
|
||||
return {
|
||||
head_ref: pr.head.ref,
|
||||
title: pr.title,
|
||||
body: pr.body
|
||||
};
|
||||
|
||||
- name: Checkout PR branch
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ fromJson(steps.pr-details.outputs.result).head_ref }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install pnpm
|
||||
run: npm install -g pnpm@10.12.4
|
||||
|
||||
- name: Notify start
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: `🤖 **Claude PR Assistant Started**\n\n💬 **Request**: ${{ github.event.comment.body }}\n🌿 **Branch**: \`${{ fromJson(steps.pr-details.outputs.result).head_ref }}\`\n\nWorking on your request...`
|
||||
});
|
||||
|
||||
- name: Assist with Claude
|
||||
uses: anthropics/claude-code-action@beta
|
||||
with:
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
direct_prompt: |
|
||||
You are assisting with a GitHub Pull Request. Please help with this request:
|
||||
|
||||
**Pull Request #${{ github.event.issue.number }}**: ${{ fromJson(steps.pr-details.outputs.result).title }}
|
||||
|
||||
**PR Description**:
|
||||
${{ fromJson(steps.pr-details.outputs.result).body }}
|
||||
|
||||
**User Request**:
|
||||
${{ github.event.comment.body }}
|
||||
|
||||
**Instructions**:
|
||||
1. Analyze the current PR changes and the user's request
|
||||
2. Implement the requested improvements, fixes, or changes
|
||||
3. Follow existing code patterns and conventions
|
||||
4. Use TypeScript with proper typing
|
||||
5. Follow ESM module structure with .js extensions
|
||||
6. Run quality checks if needed
|
||||
|
||||
This is the @xtr-dev/payload-billing plugin for PayloadCMS.
|
||||
Please implement the requested changes directly on this PR branch.
|
||||
allowed_tools: "Bash(pnpm build),Bash(pnpm typecheck),Bash(pnpm lint),Bash(npm run test)"
|
||||
|
||||
- name: Check for changes
|
||||
id: changes
|
||||
run: |
|
||||
if git diff --quiet && git diff --cached --quiet; then
|
||||
echo "has_changes=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "has_changes=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Commit and push
|
||||
if: steps.changes.outputs.has_changes == 'true'
|
||||
run: |
|
||||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "Claude PR Assistant"
|
||||
git add .
|
||||
git commit -m "feat: Claude PR assistance - ${{ github.event.comment.user.login }} request
|
||||
|
||||
Request: ${{ github.event.comment.body }}
|
||||
PR: #${{ github.event.issue.number }}
|
||||
|
||||
🤖 Generated with Claude PR Assistant
|
||||
|
||||
Co-Authored-By: Claude <noreply@anthropic.com>"
|
||||
git push origin ${{ fromJson(steps.pr-details.outputs.result).head_ref }}
|
||||
|
||||
- name: Notify success
|
||||
if: steps.changes.outputs.has_changes == 'true'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: `✅ **PR Assistance Complete!**\n\n🔧 **Changes Made**: Implemented your requested improvements\n🌿 **Branch**: \`${{ fromJson(steps.pr-details.outputs.result).head_ref }}\`\n\nChanges are ready for review! 🚀`
|
||||
});
|
||||
|
||||
- name: Handle no changes
|
||||
if: steps.changes.outputs.has_changes == 'false'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: `ℹ️ **No Changes Needed**\n\nI analyzed your request but no code changes are required.`
|
||||
});
|
||||
|
||||
- name: Handle failure
|
||||
if: failure()
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: `❌ **PR Assistance Failed**\n\nCheck the [workflow logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details.`
|
||||
});
|
||||
96
README.md
96
README.md
@@ -1,17 +1,19 @@
|
||||
# @xtr-dev/payload-billing
|
||||
|
||||
A billing and payment provider plugin for PayloadCMS 3.x. Supports Stripe, Mollie, and local testing with comprehensive tracking.
|
||||
A billing and payment provider plugin for PayloadCMS 3.x. Supports Stripe, Mollie, and local testing with comprehensive tracking and flexible customer data management.
|
||||
|
||||
⚠️ **Pre-release Warning**: This package is currently in active development (v0.0.x). Breaking changes may occur before v1.0.0. Not recommended for production use.
|
||||
⚠️ **Pre-release Warning**: This package is currently in active development (v0.1.x). Breaking changes may occur before v1.0.0. Not recommended for production use.
|
||||
|
||||
## Features
|
||||
|
||||
- 💳 Multiple payment providers (Stripe, Mollie, Test)
|
||||
- 🧾 Invoice generation and management
|
||||
- 🧾 Invoice generation and management with embedded customer info
|
||||
- 👥 Flexible customer data management with relationship support
|
||||
- 📊 Complete payment tracking and history
|
||||
- 🪝 Secure webhook processing for all providers
|
||||
- 🧪 Built-in test provider for local development
|
||||
- 📱 Payment management in PayloadCMS admin
|
||||
- 🔄 Callback-based customer data syncing
|
||||
- 🔒 Full TypeScript support
|
||||
|
||||
## Installation
|
||||
@@ -42,6 +44,8 @@ pnpm add @mollie/api-client
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Basic Configuration
|
||||
|
||||
```typescript
|
||||
import { buildConfig } from 'payload'
|
||||
import { billingPlugin, stripeProvider, mollieProvider } from '@xtr-dev/payload-billing'
|
||||
@@ -70,6 +74,68 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
### With Customer Management
|
||||
|
||||
```typescript
|
||||
import { billingPlugin, CustomerInfoExtractor } from '@xtr-dev/payload-billing'
|
||||
|
||||
// Define how to extract customer info from your customer collection
|
||||
const customerExtractor: CustomerInfoExtractor = (customer) => ({
|
||||
name: customer.name,
|
||||
email: customer.email,
|
||||
phone: customer.phone,
|
||||
company: customer.company,
|
||||
taxId: customer.taxId,
|
||||
billingAddress: {
|
||||
line1: customer.address.line1,
|
||||
line2: customer.address.line2,
|
||||
city: customer.address.city,
|
||||
state: customer.address.state,
|
||||
postalCode: customer.address.postalCode,
|
||||
country: customer.address.country,
|
||||
}
|
||||
})
|
||||
|
||||
billingPlugin({
|
||||
// ... providers
|
||||
collections: {
|
||||
payments: 'payments',
|
||||
invoices: 'invoices',
|
||||
refunds: 'refunds',
|
||||
},
|
||||
customerRelationSlug: 'customers', // Enable customer relationships
|
||||
customerInfoExtractor: customerExtractor, // Auto-sync customer data
|
||||
})
|
||||
```
|
||||
|
||||
### Custom Customer Data Extraction
|
||||
|
||||
```typescript
|
||||
import { CustomerInfoExtractor } from '@xtr-dev/payload-billing'
|
||||
|
||||
const customExtractor: CustomerInfoExtractor = (customer) => ({
|
||||
name: customer.fullName,
|
||||
email: customer.contactEmail,
|
||||
phone: customer.phoneNumber,
|
||||
company: customer.companyName,
|
||||
taxId: customer.vatNumber,
|
||||
billingAddress: {
|
||||
line1: customer.billing.street,
|
||||
line2: customer.billing.apartment,
|
||||
city: customer.billing.city,
|
||||
state: customer.billing.state,
|
||||
postalCode: customer.billing.zip,
|
||||
country: customer.billing.countryCode,
|
||||
}
|
||||
})
|
||||
|
||||
billingPlugin({
|
||||
// ... other config
|
||||
customerRelationSlug: 'clients',
|
||||
customerInfoExtractor: customExtractor,
|
||||
})
|
||||
```
|
||||
|
||||
## Imports
|
||||
|
||||
```typescript
|
||||
@@ -80,7 +146,17 @@ import { billingPlugin } from '@xtr-dev/payload-billing'
|
||||
import { stripeProvider, mollieProvider } from '@xtr-dev/payload-billing'
|
||||
|
||||
// Types
|
||||
import type { PaymentProvider, Payment, Invoice, Refund } from '@xtr-dev/payload-billing'
|
||||
import type {
|
||||
PaymentProvider,
|
||||
Payment,
|
||||
Invoice,
|
||||
Refund,
|
||||
BillingPluginConfig,
|
||||
CustomerInfoExtractor,
|
||||
MollieProviderConfig,
|
||||
StripeProviderConfig,
|
||||
ProviderData
|
||||
} from '@xtr-dev/payload-billing'
|
||||
```
|
||||
|
||||
## Provider Types
|
||||
@@ -99,9 +175,19 @@ Local development testing with configurable scenarios, automatic completion, deb
|
||||
The plugin adds these collections:
|
||||
|
||||
- **payments** - Payment transactions with status and provider data
|
||||
- **invoices** - Invoice generation with line items and PDF support
|
||||
- **invoices** - Invoice generation with line items and embedded customer info
|
||||
- **refunds** - Refund tracking and management
|
||||
|
||||
### Customer Data Management
|
||||
|
||||
The plugin supports flexible customer data handling:
|
||||
|
||||
1. **With Customer Relationship + Extractor**: Customer relationship required, customer info auto-populated and read-only, syncs automatically when customer changes
|
||||
|
||||
2. **With Customer Relationship (no extractor)**: Customer relationship optional, customer info manually editable, either relationship OR customer info required
|
||||
|
||||
3. **No Customer Collection**: Customer info fields always required and editable, no relationship field available
|
||||
|
||||
## Webhook Endpoints
|
||||
|
||||
Automatic webhook endpoints are created for configured providers:
|
||||
|
||||
@@ -158,7 +158,7 @@ export interface Payment {
|
||||
/**
|
||||
* The payment ID from the payment provider
|
||||
*/
|
||||
providerId: string;
|
||||
providerId?: string | null;
|
||||
status: 'pending' | 'processing' | 'succeeded' | 'failed' | 'canceled' | 'refunded' | 'partially_refunded';
|
||||
/**
|
||||
* Amount in cents (e.g., 2000 = $20.00)
|
||||
@@ -198,6 +198,7 @@ export interface Payment {
|
||||
| boolean
|
||||
| null;
|
||||
refunds?: (number | Refund)[] | null;
|
||||
version?: number | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
@@ -499,6 +500,7 @@ export interface PaymentsSelect<T extends boolean = true> {
|
||||
metadata?: T;
|
||||
providerData?: T;
|
||||
refunds?: T;
|
||||
version?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@xtr-dev/payload-billing",
|
||||
"version": "0.1.4",
|
||||
"version": "0.1.6",
|
||||
"description": "PayloadCMS plugin for billing and payment provider integrations with tracking and local testing",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
@@ -106,7 +106,7 @@
|
||||
"vitest": "^3.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@mollie/api-client": "^3.7.0",
|
||||
"@mollie/api-client": "^3.7.0 || ^4.0.0",
|
||||
"payload": "^3.37.0",
|
||||
"stripe": "^18.5.0"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Payment } from '@/plugin/types'
|
||||
import type { Payment } from '../plugin/types/index.js'
|
||||
import type { Payload } from 'payload'
|
||||
import { useBillingPlugin } from '@/plugin'
|
||||
import { useBillingPlugin } from '../plugin/index.js'
|
||||
|
||||
export const initProviderPayment = (payload: Payload, payment: Partial<Payment>) => {
|
||||
const billing = useBillingPlugin(payload)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export { createInvoicesCollection } from './invoices'
|
||||
export { createPaymentsCollection } from './payments'
|
||||
export { createRefundsCollection } from './refunds'
|
||||
export { createInvoicesCollection } from './invoices.js'
|
||||
export { createPaymentsCollection } from './payments.js'
|
||||
export { createRefundsCollection } from './refunds.js'
|
||||
|
||||
@@ -5,10 +5,10 @@ import {
|
||||
CollectionBeforeValidateHook,
|
||||
CollectionConfig, Field,
|
||||
} from 'payload'
|
||||
import type { BillingPluginConfig} from '@/plugin/config';
|
||||
import { defaults } from '@/plugin/config'
|
||||
import { extractSlug } from '@/plugin/utils'
|
||||
import type { Invoice } from '@/plugin/types/invoices'
|
||||
import type { BillingPluginConfig} from '../plugin/config.js';
|
||||
import { defaults } from '../plugin/config.js'
|
||||
import { extractSlug } from '../plugin/utils.js'
|
||||
import type { Invoice } from '../plugin/types/invoices.js'
|
||||
|
||||
export function createInvoicesCollection(pluginConfig: BillingPluginConfig): CollectionConfig {
|
||||
const {customerRelationSlug, customerInfoExtractor} = pluginConfig
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { AccessArgs, CollectionBeforeChangeHook, CollectionConfig, Field } from 'payload'
|
||||
import type { BillingPluginConfig} from '@/plugin/config';
|
||||
import { defaults } from '@/plugin/config'
|
||||
import { extractSlug } from '@/plugin/utils'
|
||||
import type { Payment } from '@/plugin/types/payments'
|
||||
import { initProviderPayment } from '@/collections/hooks'
|
||||
import type { BillingPluginConfig} from '../plugin/config.js';
|
||||
import { defaults } from '../plugin/config.js'
|
||||
import { extractSlug } from '../plugin/utils.js'
|
||||
import type { Payment } from '../plugin/types/payments.js'
|
||||
import { initProviderPayment } from './hooks.js'
|
||||
|
||||
export function createPaymentsCollection(pluginConfig: BillingPluginConfig): CollectionConfig {
|
||||
const overrides = typeof pluginConfig.collections?.payments === 'object' ? pluginConfig.collections?.payments : {}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { AccessArgs, CollectionConfig } from 'payload'
|
||||
import { BillingPluginConfig, defaults } from '@/plugin/config'
|
||||
import { extractSlug } from '@/plugin/utils'
|
||||
import { Payment } from '@/plugin/types'
|
||||
import { BillingPluginConfig, defaults } from '../plugin/config.js'
|
||||
import { extractSlug } from '../plugin/utils.js'
|
||||
import { Payment } from '../plugin/types/index.js'
|
||||
|
||||
export function createRefundsCollection(pluginConfig: BillingPluginConfig): CollectionConfig {
|
||||
// TODO: finish collection overrides
|
||||
|
||||
10
src/index.ts
10
src/index.ts
@@ -1,4 +1,8 @@
|
||||
|
||||
export { billingPlugin } from './plugin'
|
||||
export type { BillingPluginConfig, CustomerInfoExtractor } from './plugin/config'
|
||||
export type { Invoice, Payment, Refund } from './plugin/types'
|
||||
export { billingPlugin } from './plugin/index.js'
|
||||
export { mollieProvider, stripeProvider } from './providers/index.js'
|
||||
export type { BillingPluginConfig, CustomerInfoExtractor } from './plugin/config.js'
|
||||
export type { Invoice, Payment, Refund } from './plugin/types/index.js'
|
||||
export type { PaymentProvider, ProviderData } from './providers/types.js'
|
||||
export type { MollieProviderConfig } from './providers/mollie.js'
|
||||
export type { StripeProviderConfig } from './providers/stripe.js'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CollectionConfig } from 'payload'
|
||||
import { FieldsOverride } from '@/plugin/utils'
|
||||
import { PaymentProvider } from '@/plugin/types'
|
||||
import { FieldsOverride } from './utils.js'
|
||||
import { PaymentProvider } from './types/index.js'
|
||||
|
||||
export const defaults = {
|
||||
paymentsCollection: 'payments',
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { createInvoicesCollection, createPaymentsCollection, createRefundsCollection } from '@/collections'
|
||||
import type { BillingPluginConfig } from '@/plugin/config'
|
||||
import { createInvoicesCollection, createPaymentsCollection, createRefundsCollection } from '../collections/index.js'
|
||||
import type { BillingPluginConfig } from './config.js'
|
||||
import type { Config, Payload } from 'payload'
|
||||
import { createSingleton } from '@/plugin/singleton'
|
||||
import type { PaymentProvider } from '@/providers'
|
||||
import { createSingleton } from './singleton.js'
|
||||
import type { PaymentProvider } from '../providers/index.js'
|
||||
|
||||
const singleton = createSingleton(Symbol('billingPlugin'))
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export * from './id'
|
||||
export * from './invoices'
|
||||
export * from './payments'
|
||||
export * from './refunds'
|
||||
export * from '../../providers/types'
|
||||
export * from './id.js'
|
||||
export * from './invoices.js'
|
||||
export * from './payments.js'
|
||||
export * from './refunds.js'
|
||||
export * from '../../providers/types.js'
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Payment } from '@/plugin/types/payments'
|
||||
|
||||
import { Id } from '@/plugin/types/id'
|
||||
import { Payment } from './payments.js'
|
||||
import { Id } from './id.js'
|
||||
|
||||
export interface Invoice<TCustomer = unknown> {
|
||||
id: Id;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Refund } from '@/plugin/types/refunds'
|
||||
import { Invoice } from '@/plugin/types/invoices'
|
||||
import { Id } from '@/plugin/types/id'
|
||||
import { Refund } from './refunds.js'
|
||||
import { Invoice } from './invoices.js'
|
||||
import { Id } from './id.js'
|
||||
|
||||
export interface Payment {
|
||||
id: Id;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Payment } from '@/plugin/types/payments'
|
||||
import { Payment } from './payments.js'
|
||||
|
||||
export interface Refund {
|
||||
id: number;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { CollectionConfig, CollectionSlug, Field } from 'payload'
|
||||
import type { Id } from '@/plugin/types'
|
||||
import type { Id } from './types/index.js'
|
||||
|
||||
export type FieldsOverride = (args: { defaultFields: Field[] }) => Field[]
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export * from './mollie'
|
||||
export * from './stripe'
|
||||
export * from './types'
|
||||
export * from './currency'
|
||||
export * from './mollie.js'
|
||||
export * from './stripe.js'
|
||||
export * from './types.js'
|
||||
export * from './currency.js'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Payment } from '@/plugin/types/payments'
|
||||
import type { PaymentProvider } from '@/plugin/types'
|
||||
import type { Payment } from '../plugin/types/payments.js'
|
||||
import type { PaymentProvider } from '../plugin/types/index.js'
|
||||
import type { Payload } from 'payload'
|
||||
import { createSingleton } from '@/plugin/singleton'
|
||||
import { createSingleton } from '../plugin/singleton.js'
|
||||
import type { createMollieClient, MollieClient } from '@mollie/api-client'
|
||||
import {
|
||||
webhookResponses,
|
||||
@@ -10,8 +10,8 @@ import {
|
||||
updateInvoiceOnPaymentSuccess,
|
||||
handleWebhookError,
|
||||
validateProductionUrl
|
||||
} from './utils'
|
||||
import { formatAmountForProvider, isValidAmount, isValidCurrencyCode } from './currency'
|
||||
} from './utils.js'
|
||||
import { formatAmountForProvider, isValidAmount, isValidCurrencyCode } from './currency.js'
|
||||
|
||||
const symbol = Symbol('mollie')
|
||||
export type MollieProviderConfig = Parameters<typeof createMollieClient>[0]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Payment } from '@/plugin/types/payments'
|
||||
import type { PaymentProvider, ProviderData } from '@/plugin/types'
|
||||
import type { Payment } from '../plugin/types/payments.js'
|
||||
import type { PaymentProvider, ProviderData } from '../plugin/types/index.js'
|
||||
import type { Payload } from 'payload'
|
||||
import { createSingleton } from '@/plugin/singleton'
|
||||
import { createSingleton } from '../plugin/singleton.js'
|
||||
import type Stripe from 'stripe'
|
||||
import {
|
||||
webhookResponses,
|
||||
@@ -10,8 +10,8 @@ import {
|
||||
updateInvoiceOnPaymentSuccess,
|
||||
handleWebhookError,
|
||||
logWebhookEvent
|
||||
} from './utils'
|
||||
import { isValidAmount, isValidCurrencyCode } from './currency'
|
||||
} from './utils.js'
|
||||
import { isValidAmount, isValidCurrencyCode } from './currency.js'
|
||||
|
||||
const symbol = Symbol('stripe')
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Payment } from '@/plugin/types/payments'
|
||||
import type { Payment } from '../plugin/types/payments.js'
|
||||
import type { Config, Payload } from 'payload'
|
||||
import type { BillingPluginConfig } from '@/plugin/config'
|
||||
import type { BillingPluginConfig } from '../plugin/config.js'
|
||||
|
||||
export type InitPayment = (payload: Payload, payment: Partial<Payment>) => Promise<Partial<Payment>>
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { Payload } from 'payload'
|
||||
import type { Payment } from '@/plugin/types/payments'
|
||||
import type { BillingPluginConfig } from '@/plugin/config'
|
||||
import type { ProviderData } from './types'
|
||||
import { defaults } from '@/plugin/config'
|
||||
import { extractSlug, toPayloadId } from '@/plugin/utils'
|
||||
import type { Payment } from '../plugin/types/payments.js'
|
||||
import type { BillingPluginConfig } from '../plugin/config.js'
|
||||
import type { ProviderData } from './types.js'
|
||||
import { defaults } from '../plugin/config.js'
|
||||
import { extractSlug, toPayloadId } from '../plugin/utils.js'
|
||||
|
||||
/**
|
||||
* Common webhook response utilities
|
||||
|
||||
Reference in New Issue
Block a user