ima-claude 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +463 -0
- package/dist/cli.js +1064 -0
- package/package.json +49 -0
- package/platforms/claude/adapter.ts +115 -0
- package/platforms/junie/adapter.ts +254 -0
- package/platforms/junie/agents-template.md +113 -0
- package/platforms/junie/hook-translations.md +84 -0
- package/platforms/shared/detector.ts +27 -0
- package/platforms/shared/installer.ts +202 -0
- package/platforms/shared/types.ts +78 -0
- package/plugins/ima-claude/.claude-plugin/plugin.json +25 -0
- package/plugins/ima-claude/agents/explorer.md +30 -0
- package/plugins/ima-claude/agents/implementer.md +30 -0
- package/plugins/ima-claude/agents/memory.md +42 -0
- package/plugins/ima-claude/agents/reviewer.md +53 -0
- package/plugins/ima-claude/agents/tester.md +33 -0
- package/plugins/ima-claude/agents/wp-developer.md +46 -0
- package/plugins/ima-claude/hooks/README.md +145 -0
- package/plugins/ima-claude/hooks/atlassian_prereqs.py +112 -0
- package/plugins/ima-claude/hooks/block_sed_edits.py +59 -0
- package/plugins/ima-claude/hooks/bootstrap.sh +90 -0
- package/plugins/ima-claude/hooks/bootstrap_utility_check.py +94 -0
- package/plugins/ima-claude/hooks/composer_autoload_check.py +70 -0
- package/plugins/ima-claude/hooks/docs_organization.py +104 -0
- package/plugins/ima-claude/hooks/enforce_rg_over_grep.py +56 -0
- package/plugins/ima-claude/hooks/fp_utility_check.py +90 -0
- package/plugins/ima-claude/hooks/hook_logger.py +69 -0
- package/plugins/ima-claude/hooks/hooks.json +239 -0
- package/plugins/ima-claude/hooks/jira_issue_fetch.py +79 -0
- package/plugins/ima-claude/hooks/jquery_in_wordpress.py +92 -0
- package/plugins/ima-claude/hooks/memory_bootstrap.py +79 -0
- package/plugins/ima-claude/hooks/memory_store_reminder.py +75 -0
- package/plugins/ima-claude/hooks/prompt_coach.py +125 -0
- package/plugins/ima-claude/hooks/prompt_coach_digest.md +48 -0
- package/plugins/ima-claude/hooks/prompt_coach_system.md +30 -0
- package/plugins/ima-claude/hooks/sequential_thinking_check.py +81 -0
- package/plugins/ima-claude/hooks/serena_over_grep.py +96 -0
- package/plugins/ima-claude/hooks/serena_over_read.py +66 -0
- package/plugins/ima-claude/hooks/serena_project_check.py +133 -0
- package/plugins/ima-claude/hooks/sql_injection_check.py +73 -0
- package/plugins/ima-claude/hooks/task_master_after_plan.py +31 -0
- package/plugins/ima-claude/hooks/task_master_before_impl.py +93 -0
- package/plugins/ima-claude/hooks/tavily_extract_advanced.py +48 -0
- package/plugins/ima-claude/hooks/vestige_before_external.py +86 -0
- package/plugins/ima-claude/hooks/webfetch_to_tavily.py +42 -0
- package/plugins/ima-claude/hooks/websearch_to_tavily.py +41 -0
- package/plugins/ima-claude/hooks/wp_security_check.py +150 -0
- package/plugins/ima-claude/personalities/README.md +45 -0
- package/plugins/ima-claude/personalities/enable-40k.md +69 -0
- package/plugins/ima-claude/personalities/enable-templars.md +69 -0
- package/plugins/ima-claude/skills/.research-summary.md +340 -0
- package/plugins/ima-claude/skills/architect/SKILL.md +304 -0
- package/plugins/ima-claude/skills/compound-bridge/SKILL.md +200 -0
- package/plugins/ima-claude/skills/discourse/SKILL.md +440 -0
- package/plugins/ima-claude/skills/discourse-admin/SKILL.md +192 -0
- package/plugins/ima-claude/skills/discourse-admin/references/api-endpoints.md +441 -0
- package/plugins/ima-claude/skills/discourse-admin/references/gotchas.md +107 -0
- package/plugins/ima-claude/skills/discourse-admin/references/staging-defaults.md +98 -0
- package/plugins/ima-claude/skills/discourse-admin/scripts/discourse-admin.py +319 -0
- package/plugins/ima-claude/skills/docs-organize/SKILL.md +254 -0
- package/plugins/ima-claude/skills/docs-organize/templates/active-README.md +50 -0
- package/plugins/ima-claude/skills/docs-organize/templates/archive-README.md +57 -0
- package/plugins/ima-claude/skills/docs-organize/templates/docs-README.md +43 -0
- package/plugins/ima-claude/skills/docs-organize/templates/phase-archive-README.md +83 -0
- package/plugins/ima-claude/skills/docs-organize/templates/section-README.md +48 -0
- package/plugins/ima-claude/skills/docs-organize/templates/transient-README.md +79 -0
- package/plugins/ima-claude/skills/docs-organize/templates/transient-gitignore +9 -0
- package/plugins/ima-claude/skills/ember-discourse/SKILL.md +496 -0
- package/plugins/ima-claude/skills/functional-programmer/SKILL.md +258 -0
- package/plugins/ima-claude/skills/ima-bootstrap/SKILL.md +278 -0
- package/plugins/ima-claude/skills/ima-bootstrap/references/bootstrap-patterns.md +356 -0
- package/plugins/ima-claude/skills/ima-bootstrap/references/ima-brand.md +273 -0
- package/plugins/ima-claude/skills/ima-bootstrap/references/theme-integration.md +212 -0
- package/plugins/ima-claude/skills/ima-brand/SKILL.md +108 -0
- package/plugins/ima-claude/skills/ima-brand/references/brand-identity.md +140 -0
- package/plugins/ima-claude/skills/ima-brand/references/digital-standards.md +180 -0
- package/plugins/ima-claude/skills/ima-brand/references/visual-system.md +173 -0
- package/plugins/ima-claude/skills/ima-forms-expert/SKILL.md +175 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/container-components.md +154 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/examples.md +328 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/field-components.md +298 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/form-factory.md +193 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/quick-reference.md +153 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/validation-engine.md +336 -0
- package/plugins/ima-claude/skills/jira-checkpoint/SKILL.md +178 -0
- package/plugins/ima-claude/skills/jquery/SKILL.md +413 -0
- package/plugins/ima-claude/skills/js-fp/SKILL.md +463 -0
- package/plugins/ima-claude/skills/js-fp/core-principles.md +487 -0
- package/plugins/ima-claude/skills/js-fp/examples/pure-functions.js +260 -0
- package/plugins/ima-claude/skills/js-fp/examples/tests/pure-functions.test.js +262 -0
- package/plugins/ima-claude/skills/js-fp/references/anti-patterns.md +120 -0
- package/plugins/ima-claude/skills/js-fp/references/performance-patterns.md +116 -0
- package/plugins/ima-claude/skills/js-fp/references/testing-patterns.md +134 -0
- package/plugins/ima-claude/skills/js-fp-api/SKILL.md +280 -0
- package/plugins/ima-claude/skills/js-fp-api/examples/crud-endpoint.js +258 -0
- package/plugins/ima-claude/skills/js-fp-api/references/middleware-patterns.md +134 -0
- package/plugins/ima-claude/skills/js-fp-api/references/security-sql.md +110 -0
- package/plugins/ima-claude/skills/js-fp-api/references/validation-patterns.md +165 -0
- package/plugins/ima-claude/skills/js-fp-react/SKILL.md +447 -0
- package/plugins/ima-claude/skills/js-fp-react/examples/ProductCard.tsx +65 -0
- package/plugins/ima-claude/skills/js-fp-react/references/hooks-advanced.md +136 -0
- package/plugins/ima-claude/skills/js-fp-react/references/performance-patterns.md +175 -0
- package/plugins/ima-claude/skills/js-fp-vue/SKILL.md +322 -0
- package/plugins/ima-claude/skills/js-fp-vue/references/complete-examples.md +397 -0
- package/plugins/ima-claude/skills/js-fp-vue/references/composables-advanced.md +282 -0
- package/plugins/ima-claude/skills/js-fp-vue/references/reactivity-patterns.md +348 -0
- package/plugins/ima-claude/skills/js-fp-vue/references/testing.md +314 -0
- package/plugins/ima-claude/skills/js-fp-wordpress/SKILL.md +301 -0
- package/plugins/ima-claude/skills/js-fp-wordpress/references/ajax-patterns.md +192 -0
- package/plugins/ima-claude/skills/js-fp-wordpress/references/event-patterns.md +136 -0
- package/plugins/ima-claude/skills/js-fp-wordpress/references/wp-integration.md +248 -0
- package/plugins/ima-claude/skills/livecanvas/SKILL.md +209 -0
- package/plugins/ima-claude/skills/livecanvas/references/livecanvas-features.md +311 -0
- package/plugins/ima-claude/skills/livecanvas/references/loops-and-logic.md +730 -0
- package/plugins/ima-claude/skills/livecanvas/references/picostrap.md +227 -0
- package/plugins/ima-claude/skills/mcp-atlassian/SKILL.md +339 -0
- package/plugins/ima-claude/skills/mcp-context7/SKILL.md +109 -0
- package/plugins/ima-claude/skills/mcp-memory/SKILL.md +182 -0
- package/plugins/ima-claude/skills/mcp-qdrant/SKILL.md +233 -0
- package/plugins/ima-claude/skills/mcp-sequential/SKILL.md +149 -0
- package/plugins/ima-claude/skills/mcp-serena/SKILL.md +174 -0
- package/plugins/ima-claude/skills/mcp-tavily/SKILL.md +118 -0
- package/plugins/ima-claude/skills/mcp-vestige/SKILL.md +259 -0
- package/plugins/ima-claude/skills/php-authnet/SKILL.md +275 -0
- package/plugins/ima-claude/skills/php-authnet/references/api-reference.md +624 -0
- package/plugins/ima-claude/skills/php-authnet/references/sandbox-testing.md +424 -0
- package/plugins/ima-claude/skills/php-fp/SKILL.md +333 -0
- package/plugins/ima-claude/skills/php-fp/examples/pure-functions.php +403 -0
- package/plugins/ima-claude/skills/php-fp/examples/tests/PureFunctionsTest.php +515 -0
- package/plugins/ima-claude/skills/php-fp/references/core-principles.md +277 -0
- package/plugins/ima-claude/skills/php-fp/references/testing-patterns.md +374 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/SKILL.md +216 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/references/fp-patterns.md +275 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/references/plugin-architecture.md +295 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/references/security-examples.md +203 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/references/testing-strategy.md +259 -0
- package/plugins/ima-claude/skills/phpunit-wp/SKILL.md +716 -0
- package/plugins/ima-claude/skills/playwright/SKILL.md +434 -0
- package/plugins/ima-claude/skills/playwright/references/accessibility-testing.md +153 -0
- package/plugins/ima-claude/skills/playwright/references/ci-cd.md +268 -0
- package/plugins/ima-claude/skills/playwright/references/network-mocking.md +270 -0
- package/plugins/ima-claude/skills/playwright/references/visual-regression.md +215 -0
- package/plugins/ima-claude/skills/py-fp/SKILL.md +663 -0
- package/plugins/ima-claude/skills/py-fp/examples/pure-functions.py +185 -0
- package/plugins/ima-claude/skills/py-fp/examples/tests/test_pure_functions.py +244 -0
- package/plugins/ima-claude/skills/py-fp/references/core-principles.md +381 -0
- package/plugins/ima-claude/skills/py-fp/references/testing-patterns.md +283 -0
- package/plugins/ima-claude/skills/quasar-fp/SKILL.md +327 -0
- package/plugins/ima-claude/skills/quasar-fp/metadata.json +85 -0
- package/plugins/ima-claude/skills/quasar-fp/references/component-patterns.md +257 -0
- package/plugins/ima-claude/skills/quasar-fp/references/theme-integration.md +233 -0
- package/plugins/ima-claude/skills/quasar-fp/references/utility-classes.md +237 -0
- package/plugins/ima-claude/skills/quickstart/SKILL.md +129 -0
- package/plugins/ima-claude/skills/rails/SKILL.md +359 -0
- package/plugins/ima-claude/skills/resume-session/SKILL.md +68 -0
- package/plugins/ima-claude/skills/rg/SKILL.md +205 -0
- package/plugins/ima-claude/skills/ruby-fp/SKILL.md +336 -0
- package/plugins/ima-claude/skills/save-session/SKILL.md +81 -0
- package/plugins/ima-claude/skills/scorecard/SKILL.md +96 -0
- package/plugins/ima-claude/skills/skill-analyzer/SKILL.md +127 -0
- package/plugins/ima-claude/skills/skill-analyzer/references/advanced-checklist.md +44 -0
- package/plugins/ima-claude/skills/skill-analyzer/references/core-checklist.md +60 -0
- package/plugins/ima-claude/skills/skill-analyzer/scripts/analyze_skill.py +418 -0
- package/plugins/ima-claude/skills/skill-creator/LICENSE.txt +202 -0
- package/plugins/ima-claude/skills/skill-creator/SKILL.md +343 -0
- package/plugins/ima-claude/skills/skill-creator/references/output-patterns.md +82 -0
- package/plugins/ima-claude/skills/skill-creator/references/workflows.md +28 -0
- package/plugins/ima-claude/skills/skill-creator/scripts/init_skill.py +303 -0
- package/plugins/ima-claude/skills/skill-creator/scripts/package_skill.py +110 -0
- package/plugins/ima-claude/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/plugins/ima-claude/skills/task-master/SKILL.md +51 -0
- package/plugins/ima-claude/skills/task-planner/SKILL.md +228 -0
- package/plugins/ima-claude/skills/task-runner/SKILL.md +192 -0
- package/plugins/ima-claude/skills/unit-testing/SKILL.md +198 -0
- package/plugins/ima-claude/skills/unit-testing/references/mock-patterns.md +181 -0
- package/plugins/ima-claude/skills/unit-testing/references/tdd-workflow.md +177 -0
- package/plugins/ima-claude/skills/unit-testing/references/test-strategy.md +126 -0
- package/plugins/ima-claude/skills/wp-local/SKILL.md +246 -0
- package/plugins/ima-claude/skills/wp-local/references/configuration.md +198 -0
- package/plugins/ima-claude/skills/wp-local/references/wp-cli-reference.md +406 -0
- package/plugins/ima-claude/skills/wp-local/scripts/wp-local.sh +61 -0
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
# Authorize.Net Sandbox & Testing Guide
|
|
2
|
+
|
|
3
|
+
Testing patterns, sandbox configuration, test card numbers, and PHPUnit strategies for Authorize.Net PHP SDK.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Sandbox Setup](#sandbox-setup)
|
|
8
|
+
- [Test Card Numbers](#test-card-numbers)
|
|
9
|
+
- [Sandbox Gotchas](#sandbox-gotchas)
|
|
10
|
+
- [PHPUnit Testing Patterns](#phpunit-testing-patterns)
|
|
11
|
+
- [Test Data Reference](#test-data-reference)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Sandbox Setup
|
|
16
|
+
|
|
17
|
+
### Sandbox Credentials
|
|
18
|
+
|
|
19
|
+
1. Create sandbox account at https://developer.authorize.net/hello_world/sandbox.html
|
|
20
|
+
2. Get API Login ID + Transaction Key from Account → Settings → API Credentials & Keys
|
|
21
|
+
3. Get Public Client Key for Accept.js from Account → Settings → Manage Public Client Key
|
|
22
|
+
4. Get Signature Key for webhooks from Account → Settings → API Credentials & Keys → New Signature Key
|
|
23
|
+
|
|
24
|
+
### Environment URLs
|
|
25
|
+
|
|
26
|
+
```php
|
|
27
|
+
use net\authorize\api\constants\ANetEnvironment;
|
|
28
|
+
|
|
29
|
+
// Sandbox
|
|
30
|
+
$api_url = ANetEnvironment::SANDBOX;
|
|
31
|
+
// https://apitest.authorize.net/xml/v1/request.api
|
|
32
|
+
|
|
33
|
+
// Production
|
|
34
|
+
$api_url = ANetEnvironment::PRODUCTION;
|
|
35
|
+
// https://api.authorize.net/xml/v1/request.api
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Accept.js Script URLs
|
|
39
|
+
|
|
40
|
+
```html
|
|
41
|
+
<!-- Sandbox -->
|
|
42
|
+
<script src="https://jstest.authorize.net/v1/Accept.js"></script>
|
|
43
|
+
|
|
44
|
+
<!-- Production -->
|
|
45
|
+
<script src="https://js.authorize.net/v1/Accept.js"></script>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Test Card Numbers
|
|
51
|
+
|
|
52
|
+
### Credit Cards
|
|
53
|
+
|
|
54
|
+
| Card Type | Number | CVV | Result |
|
|
55
|
+
|-----------|--------|-----|--------|
|
|
56
|
+
| Visa | `4111111111111111` | Any 3-digit | Approved |
|
|
57
|
+
| Visa | `4222222222222` | Any 3-digit | Approved |
|
|
58
|
+
| Mastercard | `5424000000000015` | Any 3-digit | Approved |
|
|
59
|
+
| Amex | `370000000000002` | Any 4-digit | Approved |
|
|
60
|
+
| Discover | `6011000000000012` | Any 3-digit | Approved |
|
|
61
|
+
| JCB | `3088000000000017` | Any 3-digit | Approved |
|
|
62
|
+
| Diners Club | `38000000000006` | Any 3-digit | Approved |
|
|
63
|
+
|
|
64
|
+
### Triggering Declines
|
|
65
|
+
|
|
66
|
+
Use specific amounts to trigger decline responses:
|
|
67
|
+
|
|
68
|
+
| Amount ending in | Response |
|
|
69
|
+
|-----------------|----------|
|
|
70
|
+
| `.00` | Approved (default) |
|
|
71
|
+
| `.01` | Approved |
|
|
72
|
+
| `.02` | Declined |
|
|
73
|
+
| `.03` | Error |
|
|
74
|
+
| `.04` | Held for review |
|
|
75
|
+
|
|
76
|
+
Example: `$50.02` triggers a decline.
|
|
77
|
+
|
|
78
|
+
### Expiration Dates
|
|
79
|
+
|
|
80
|
+
Any future date works in sandbox. Use `12/2030` or similar.
|
|
81
|
+
|
|
82
|
+
### eCheck (ACH)
|
|
83
|
+
|
|
84
|
+
| Field | Test Value |
|
|
85
|
+
|-------|-----------|
|
|
86
|
+
| Routing Number | `121042882` |
|
|
87
|
+
| Account Number | Any 6-17 digit number |
|
|
88
|
+
| Account Type | `checking` or `savings` |
|
|
89
|
+
| Name on Account | Any name |
|
|
90
|
+
| eCheck Type | `WEB` (internet) or `PPD` (prearranged) |
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Sandbox Gotchas
|
|
95
|
+
|
|
96
|
+
### CIM → ARB Propagation Delay
|
|
97
|
+
|
|
98
|
+
**Problem**: Creating a CIM profile then immediately creating an ARB subscription with that profile fails with "The record cannot be found."
|
|
99
|
+
|
|
100
|
+
**Cause**: Sandbox takes ~15 seconds to propagate CIM profiles to the ARB subsystem.
|
|
101
|
+
|
|
102
|
+
**Solution**: Retry with delay (sandbox only):
|
|
103
|
+
|
|
104
|
+
```php
|
|
105
|
+
function create_subscription_with_retry(
|
|
106
|
+
string $customer_profile_id,
|
|
107
|
+
string $payment_profile_id,
|
|
108
|
+
array $plan,
|
|
109
|
+
array $credentials,
|
|
110
|
+
string $environment
|
|
111
|
+
): array {
|
|
112
|
+
$max_attempts = ($environment === 'sandbox') ? 3 : 1;
|
|
113
|
+
|
|
114
|
+
for ($attempt = 1; $attempt <= $max_attempts; $attempt++) {
|
|
115
|
+
$result = create_subscription_via_sdk(
|
|
116
|
+
$customer_profile_id, $payment_profile_id, $plan, $credentials
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
if ($result['success']) return $result;
|
|
120
|
+
if ($attempt === $max_attempts) return $result;
|
|
121
|
+
if (!str_contains($result['error'] ?? '', 'record cannot be found')) return $result;
|
|
122
|
+
|
|
123
|
+
sleep(15); // wait for propagation
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return ['success' => false, 'error' => 'Failed after retries'];
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Validation Mode with Accept.js Tokens
|
|
131
|
+
|
|
132
|
+
**Problem**: `liveMode` validation fails with Accept.js opaque data in sandbox.
|
|
133
|
+
|
|
134
|
+
**Solution**: Use `testMode` in sandbox, `liveMode` in production:
|
|
135
|
+
|
|
136
|
+
```php
|
|
137
|
+
$validation_mode = ($environment === 'sandbox') ? 'testMode' : 'liveMode';
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Settlement Timing
|
|
141
|
+
|
|
142
|
+
Sandbox settles transactions daily at a specific time (not real-time). This affects:
|
|
143
|
+
- Refunds: Can only refund settled transactions
|
|
144
|
+
- Voids: Can only void unsettled transactions
|
|
145
|
+
- You cannot test both refund and void on the same transaction in the same test run
|
|
146
|
+
|
|
147
|
+
### Webhook Delivery in Sandbox
|
|
148
|
+
|
|
149
|
+
- Webhooks work in sandbox but may have higher latency
|
|
150
|
+
- Webhook URL must be publicly accessible (use ngrok or similar for local dev)
|
|
151
|
+
- Sandbox webhook signature key is different from production
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## PHPUnit Testing Patterns
|
|
156
|
+
|
|
157
|
+
### Testing the Three-Layer Architecture
|
|
158
|
+
|
|
159
|
+
The FP architecture makes testing straightforward — pure functions need no mocks.
|
|
160
|
+
|
|
161
|
+
#### Testing Pure Builders (No Mocks Needed)
|
|
162
|
+
|
|
163
|
+
```php
|
|
164
|
+
<?php declare(strict_types=1);
|
|
165
|
+
|
|
166
|
+
use PHPUnit\Framework\TestCase;
|
|
167
|
+
|
|
168
|
+
class RequestBuilderTest extends TestCase
|
|
169
|
+
{
|
|
170
|
+
public function test_build_charge_request_normalizes_billing(): void
|
|
171
|
+
{
|
|
172
|
+
$result = build_charge_request([
|
|
173
|
+
'amount' => 29.99,
|
|
174
|
+
'payment_token' => 'token123',
|
|
175
|
+
'billing' => ['first_name' => 'Jane', 'last_name' => 'Doe'],
|
|
176
|
+
]);
|
|
177
|
+
|
|
178
|
+
$this->assertSame(29.99, $result['amount']);
|
|
179
|
+
$this->assertSame('token123', $result['payment_token']);
|
|
180
|
+
$this->assertSame('Jane', $result['billing']['first_name']);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
public function test_build_charge_request_handles_empty_billing(): void
|
|
184
|
+
{
|
|
185
|
+
$result = build_charge_request([
|
|
186
|
+
'amount' => 10.00,
|
|
187
|
+
'payment_token' => 'token456',
|
|
188
|
+
'billing' => [],
|
|
189
|
+
]);
|
|
190
|
+
|
|
191
|
+
$this->assertNull($result['billing']);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
public function test_build_subscription_request_truncates_name(): void
|
|
195
|
+
{
|
|
196
|
+
$long_name = str_repeat('A', 60);
|
|
197
|
+
$result = build_subscription_request('cp1', 'pp1', [
|
|
198
|
+
'amount' => 9.99,
|
|
199
|
+
'name' => $long_name,
|
|
200
|
+
], '2025-01-01');
|
|
201
|
+
|
|
202
|
+
$this->assertSame(50, strlen($result['name']));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
#### Testing Pure Parsers (No Mocks Needed)
|
|
208
|
+
|
|
209
|
+
```php
|
|
210
|
+
class ResponseParserTest extends TestCase
|
|
211
|
+
{
|
|
212
|
+
public function test_parse_charge_success(): void
|
|
213
|
+
{
|
|
214
|
+
$result = parse_charge_response([
|
|
215
|
+
'result_code' => 'Ok',
|
|
216
|
+
'transaction_id' => 'TX123',
|
|
217
|
+
'has_transaction_messages' => true,
|
|
218
|
+
'auth_code' => 'AUTH1',
|
|
219
|
+
'message' => 'Approved',
|
|
220
|
+
'response_code' => '1',
|
|
221
|
+
'account_number' => 'XXXX1234',
|
|
222
|
+
'error_text' => null,
|
|
223
|
+
'error_code' => null,
|
|
224
|
+
]);
|
|
225
|
+
|
|
226
|
+
$this->assertTrue($result['success']);
|
|
227
|
+
$this->assertSame('TX123', $result['transaction_id']);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
public function test_parse_charge_null_response(): void
|
|
231
|
+
{
|
|
232
|
+
$result = parse_charge_response([
|
|
233
|
+
'result_code' => null,
|
|
234
|
+
'transaction_id' => null,
|
|
235
|
+
'has_transaction_messages' => false,
|
|
236
|
+
'error_text' => null,
|
|
237
|
+
'error_code' => null,
|
|
238
|
+
]);
|
|
239
|
+
|
|
240
|
+
$this->assertFalse($result['success']);
|
|
241
|
+
$this->assertSame('No response from payment gateway', $result['error']);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
public function test_parse_create_profile_duplicate_recovery(): void
|
|
245
|
+
{
|
|
246
|
+
$result = parse_create_profile_response([
|
|
247
|
+
'result_code' => 'Error',
|
|
248
|
+
'customer_profile_id' => null,
|
|
249
|
+
'payment_profile_id' => null,
|
|
250
|
+
'error_code' => 'E00039',
|
|
251
|
+
'error_text' => 'A duplicate record with ID 12345 already exists.',
|
|
252
|
+
]);
|
|
253
|
+
|
|
254
|
+
$this->assertFalse($result['success']);
|
|
255
|
+
$this->assertSame('E00039', $result['error_code']);
|
|
256
|
+
$this->assertSame('12345', $result['existing_profile_id']);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
#### Testing Pure Webhook Functions (No Mocks Needed)
|
|
262
|
+
|
|
263
|
+
```php
|
|
264
|
+
class WebhookPureFunctionsTest extends TestCase
|
|
265
|
+
{
|
|
266
|
+
public function test_validate_signature_valid(): void
|
|
267
|
+
{
|
|
268
|
+
$body = '{"eventType":"net.authorize.payment.authcapture.created"}';
|
|
269
|
+
$key = 'test-signature-key';
|
|
270
|
+
$hash = strtoupper(hash_hmac('sha512', $body, $key));
|
|
271
|
+
|
|
272
|
+
$this->assertTrue(
|
|
273
|
+
validate_webhook_signature($body, "sha512={$hash}", $key)
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
public function test_validate_signature_invalid(): void
|
|
278
|
+
{
|
|
279
|
+
$this->assertFalse(
|
|
280
|
+
validate_webhook_signature('body', 'sha512=WRONG', 'key')
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* @dataProvider eventCategoryProvider
|
|
286
|
+
*/
|
|
287
|
+
public function test_get_event_category(string $event, string|false $expected): void
|
|
288
|
+
{
|
|
289
|
+
$this->assertSame($expected, get_event_category($event));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
public static function eventCategoryProvider(): array
|
|
293
|
+
{
|
|
294
|
+
return [
|
|
295
|
+
['net.authorize.payment.authcapture.created', 'payment'],
|
|
296
|
+
['net.authorize.customer.subscription.created', 'subscription'],
|
|
297
|
+
['net.authorize.payment.fraud.held', 'fraud'],
|
|
298
|
+
['net.authorize.unknown.something', false],
|
|
299
|
+
];
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
#### Testing Validation Functions (No Mocks Needed)
|
|
305
|
+
|
|
306
|
+
```php
|
|
307
|
+
class ValidationTest extends TestCase
|
|
308
|
+
{
|
|
309
|
+
/**
|
|
310
|
+
* @dataProvider amountProvider
|
|
311
|
+
*/
|
|
312
|
+
public function test_validate_amount(mixed $amount, bool $expected_valid): void
|
|
313
|
+
{
|
|
314
|
+
$result = validate_amount($amount);
|
|
315
|
+
$this->assertSame($expected_valid, $result['valid']);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
public static function amountProvider(): array
|
|
319
|
+
{
|
|
320
|
+
return [
|
|
321
|
+
'valid' => [29.99, true],
|
|
322
|
+
'minimum' => [1.00, true],
|
|
323
|
+
'below' => [0.50, false],
|
|
324
|
+
'zero' => [0, false],
|
|
325
|
+
'negative' => [-10, false],
|
|
326
|
+
'string' => ['abc', false],
|
|
327
|
+
'max' => [25000.00, true],
|
|
328
|
+
'over_max' => [25001.00, false],
|
|
329
|
+
];
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
public function test_validate_payment_token_too_short(): void
|
|
333
|
+
{
|
|
334
|
+
$result = validate_payment_token('short');
|
|
335
|
+
$this->assertFalse($result['valid']);
|
|
336
|
+
$this->assertSame('Payment token format is invalid', $result['message']);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### Testing SDK Adapters (Integration Tests)
|
|
342
|
+
|
|
343
|
+
SDK adapter functions require the actual SDK. Test these as integration tests against the sandbox:
|
|
344
|
+
|
|
345
|
+
```php
|
|
346
|
+
/**
|
|
347
|
+
* @group integration
|
|
348
|
+
* @group requires-sandbox
|
|
349
|
+
*/
|
|
350
|
+
class SdkAdapterIntegrationTest extends TestCase
|
|
351
|
+
{
|
|
352
|
+
private array $credentials;
|
|
353
|
+
|
|
354
|
+
protected function setUp(): void
|
|
355
|
+
{
|
|
356
|
+
$this->credentials = [
|
|
357
|
+
'api_login_id' => getenv('AUTHNET_API_LOGIN_ID'),
|
|
358
|
+
'transaction_key' => getenv('AUTHNET_TRANSACTION_KEY'),
|
|
359
|
+
'api_url' => ANetEnvironment::SANDBOX,
|
|
360
|
+
];
|
|
361
|
+
|
|
362
|
+
if (empty($this->credentials['api_login_id'])) {
|
|
363
|
+
$this->markTestSkipped('Sandbox credentials not configured');
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
public function test_connection(): void
|
|
368
|
+
{
|
|
369
|
+
$result = sdk_test_connection($this->credentials);
|
|
370
|
+
$this->assertSame('Ok', $result['result_code']);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Run integration tests separately:
|
|
376
|
+
|
|
377
|
+
```bash
|
|
378
|
+
# Unit tests only (fast, no network)
|
|
379
|
+
phpunit --exclude-group=integration
|
|
380
|
+
|
|
381
|
+
# Integration tests (requires sandbox credentials)
|
|
382
|
+
AUTHNET_API_LOGIN_ID=xxx AUTHNET_TRANSACTION_KEY=yyy phpunit --group=integration
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## Test Data Reference
|
|
388
|
+
|
|
389
|
+
### Billing Address (Test)
|
|
390
|
+
|
|
391
|
+
```php
|
|
392
|
+
$test_billing = [
|
|
393
|
+
'first_name' => 'Test',
|
|
394
|
+
'last_name' => 'User',
|
|
395
|
+
'email' => 'test@example.com',
|
|
396
|
+
'company' => 'Test Company',
|
|
397
|
+
'address' => '123 Test St',
|
|
398
|
+
'city' => 'Testville',
|
|
399
|
+
'state' => 'CA',
|
|
400
|
+
'zip' => '90210',
|
|
401
|
+
'country' => 'US',
|
|
402
|
+
];
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Subscription Plan (Test)
|
|
406
|
+
|
|
407
|
+
```php
|
|
408
|
+
$test_plan = [
|
|
409
|
+
'amount' => 9.99,
|
|
410
|
+
'name' => 'Test Monthly',
|
|
411
|
+
'interval_length' => 1,
|
|
412
|
+
'interval_unit' => 'months',
|
|
413
|
+
'total_occurrences' => 12,
|
|
414
|
+
'start_date' => date('Y-m-d', strtotime('+1 day')),
|
|
415
|
+
];
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Accept.js Token (Test - Sandbox Only)
|
|
419
|
+
|
|
420
|
+
In sandbox, you can generate test tokens using the Accept.js test page or by calling Accept.js with test card data. For unit tests that need a token format but won't hit the API, use a placeholder:
|
|
421
|
+
|
|
422
|
+
```php
|
|
423
|
+
$test_token = str_repeat('A', 100); // meets length validation
|
|
424
|
+
```
|