کاربر دکمه پرداخت را میزند، پاسخ دیر میرسد و Client دوباره درخواست میفرستد. Callback هم دو بار تحویل میشود. صفحه Success است، اما آیا پول یک بار ثبت شده یا سه بار؟ وضعیت «موفق» دقیقاً به Authorization، Capture، Settlement یا واریز به ذینفع اشاره میکند؟ تست نرمافزار مالی زمانی ارزش دارد که از نمایش ظاهری فراتر برود و برای هر واحد پول، رویداد، ثبت دفترکل، وضعیت، مانده و مغایرت شواهد قابلردگیری بسازد.
این راهنما بر هستهای تمرکز دارد که بسیاری از مقالههای عمومی فینتک نادیده میگیرند: Money Contract، Transaction State Machine، Idempotency، Double-entry Ledger، Settlement و Reconciliation. از تعریف مبلغ و Currency شروع میکنیم، سپس concurrency، duplicate، refund، fee، rounding، close، migration، downtime، authorization و Release Evidence را با یک آزمایش کاملاً ساختگی به هم متصل میکنیم.
مرز مسئولیت: این مطلب آموزش تست نرمافزار است، نه مشاوره مالی، حسابداری، حقوقی، امنیتی یا مقرراتی. QA نمیتواند با Pass شدن تست، صحت صورتهای مالی، انطباق سازمان، امنیت پول یا نبود زیان را تضمین کند. Product scope، Accounting policy، Currency/rounding rule، مجوزها، قانون و استاندارد قابل اعمال، Risk appetite و پذیرش ریسک باید توسط صاحبان صلاحیت تعیین و نسخهدار شوند؛ QA شواهد همان قرارداد را تولید میکند.
خلاصه عملی تست نرمافزار مالی
| پرسش | شاهد ضعیف | شاهد تصمیمساز |
|---|---|---|
| آیا پرداخت موفق است؟ | صفحه Success یا HTTP ۲۰۰ | State مصوب، اثر مالی یکتا، Ledger postings، provider evidence و reconciliation |
| آیا مبلغ درست است؟ | متن «۱۰۰ هزار» | amount units + currency + scale + rounding policy + breakdown |
| آیا retry امن است؟ | در Happy Path دوبار نشد | timeout-after-commit، idempotency scope، fingerprint، replay و conflict آزموده شد |
| آیا دفترکل صحیح است؟ | Balance نهایی منطقی است | immutable postings، Debit=Credit، source event، reversal و period controls |
| آیا Settlement کامل است؟ | فایل وارد شد | completeness، duplicate، missing، amount/state mismatch، exception و aging روشن است |
| آیا Release آماده است؟ | Pass rate بالا | Invariantهای حیاتی، Unknownها، open exposure، rollback/reconcile و تصمیم مالک ثبت شدهاند |
مرز صفحه: نرمافزار مالی، فینتک و درگاه پرداخت یکی نیستند
Accounting، کیف پول، Billing، Lending، Treasury، Trading، Insurance، Payroll، پرداخت و بانکداری Failure mode و Oracleهای متفاوت دارند. همین محصول نیز ممکن است فقط یک Instruction ثبت کند یا واقعاً پول، تعهد، دارایی یا گزارش رسمی را تغییر دهد. قبل از Test Plan، System of Record، مالک پول و لحظه اثر مالی را مشخص کنید.
| دامنه | پرسش اصلی | Invariant نمونه | Owner نمونه |
|---|---|---|---|
| Accounting/ERP | کدام Posting و Period منبع حقیقت است؟ | سند تراز، immutable history، close control | Finance/Accounting |
| Payment | Intent، authorization، capture و settlement کجا جدا میشوند؟ | هر اثر دقیقاً یک بار در business semantics | Payments/Product/Finance |
| Wallet/Ledger | Balance چگونه از Postingها مشتق میشود؟ | Debit=Credit و no orphan posting | Ledger/Finance |
| Lending | Schedule، accrual، payment allocation و correction چه Contractی دارند؟ | نسخه Policy و traceable calculation | Credit/Finance/Legal |
| Trading | Order، execution، position و settlement چگونه وصلاند؟ | quantity/price/fee/cancel lifecycle | Trading/Risk/Operations |
| Reporting | کدام snapshot، cut-off و mapping گزارش را میسازد؟ | source-to-report lineage و reproducibility | Finance/Compliance/Data |
راهنمای تست فینتک مالک راهبرد گسترده Compliance، Security و حجم تراکنش است و راهنمای تست درگاه پرداخت روی IPG و Sandbox تمرکز دارد. صفحه حاضر مالک درستی پول، دفترکل و Reconciliation است تا این صفحات یکدیگر را تکرار نکنند.
تست مالی چه چیزی را اثبات میکند و چه چیزی را نه؟
یک Run معتبر میتواند بگوید: «برای Rule set، Currency، Timestamp، Account graph، Build، Config و Fixture مشخص، Postingها با Oracle نسخه X مطابق بودند.» این گزاره محدود است. داده واقعی، رفتار Partner، تغییر نرخ یا قانون، concurrency دیگر، خطای انسانی و مسیر اجرانشده میتوانند نتیجه متفاوت بسازند.
- Pass شدن Calculation، Accounting policy را تصویب نمیکند.
- تراز بودن Debit و Credit، درست بودن Account، Amount یا Business purpose را ثابت نمیکند.
- HTTP ۲۰۰ یا Callback معتبر، Settlement نهایی را ثابت نمیکند.
- نبود مغایرت در یک روز، Completeness تاریخی یا نبود fraud را تضمین نمیکند.
- اسکن امنیتی یا Penetration Test، منطق مالی درست را ثابت نمیکند.
- PCI DSS، AML/KYC، Privacy یا هر الزام دیگر فقط پس از تصمیم Applicability وارد Test Plan میشود.
- Automation تعداد بیشتری Example اجرا میکند؛ صحت Oracle و پوشش ریسک را خودکار تضمین نمیکند.
Financial Context Contract بسازید
تست از Context شروع میشود. مبلغی که در یک Billing system «طلب» است ممکن است در Payment system هنوز «Intent» و در Bank statement «Settled» باشد. اصطلاح Success بدون Stage و Source مبهم است.
Financial Context Contract Product/function: [نام و نسخه] Business event: [purchase/refund/fee/...] Economic meaning: [تعهد/دریافت/پرداخت/انتقال] Systems of record: [intent, ledger, provider, bank, report] Money owner/custodian: [نقش سازمانی] Currencies/scale: [canonical definitions] Accounting policy: [version + approver] State model: [version + terminal states] Cut-off/timezone: [event/effective/posted/settled] Partners/interfaces: [contract versions] Applicable controls: [legal/security/compliance decision] Excluded use: [خارج Scope] Release identities: Risk/evidence owners: [نام نقشها]
Money Contract: عدد بدون Currency و Scale پول نیست
فیلد `amount=۱۰۰۰۰۰` بهتنهایی Oracle ندارد. باید معلوم باشد Currency چیست، عدد در واحد canonical ذخیره میشود یا واحد نمایشی، scale چند است، sign چه معنایی دارد، precision کجاست و rounding در کدام مرحله و با کدام Policy اعمال میشود. تومانِ UI نباید بیصدا با ریال API یا Ledger مخلوط شود.
Money Contract amountUnits: integer/decimal representation currency: ISO/business code approved for the product scale: explicit; never inferred from UI label displayUnit: canonical / toman / other presentation sign semantics: debit/credit or inflow/outflow contract roundingMode: versioned business/accounting rule roundingPoint: line/item/tax/invoice/settlement precision: intermediate and persisted precision breakdown: principal + fee + tax + discount + adjustment exchangeRate: source, pair, direction, time and version effectiveTime: business time and timezone policyVersion: immutable reference
| Case | خطر | Oracle لازم |
|---|---|---|
| IRR در API، تومان در UI | ضریب ۱۰ یا label اشتباه | canonical amount + conversion/display rule |
| Decimal از Float | خطای نمایش دودویی/جمع | decimal/integer model و tolerance ممنوع یا مصوب |
| Fee روی هر line یا total | اختلاف rounding | rounding point و allocation policy |
| Negative amount | refund و reversal یکی گرفته میشوند | event type + posting direction |
| Currency ناشناخته | default خاموش | reject/quarantine؛ بدون حدس |
| عدد فارسی/لاتین | parse یا separator غلط | input normalization و canonical persistence جدا |
Rounding را Property و Metamorphic تست کنید
چند Example معمول برای Rounding کافی نیست. مرزهای نصف واحد، علامت منفی، جمع lineها، تقسیم نامساوی، refund جزئی، نرخ تبدیل و اجرای دوباره را پوشش دهید. Expected value باید از یک Oracle مستقل و Rule مصوب بیاید، نه همان Function تولید که نرمافزار استفاده میکند.
- مجموع allocationها دقیقاً با total مصوب برابر است.
- Permutation lineها نتیجه را تغییر نمیدهد، مگر Policy ترتیبمحور باشد.
- Refund کامل از نظر economic effect با reversal مصوب تطبیق دارد، نه لزوماً همان Event type.
- تقسیم و جمع مجدد، residual unit را طبق policy به مقصد مشخص میدهد.
- تبدیل A→B→A لزوماً identity نیست؛ spread/rounding و tolerance باید مصوب باشند.
- اجرای یک Rule set با ورودی و Version ثابت deterministic است.
- Migration یا تغییر library بدون approval نتیجه historical را بازنویسی نمیکند.
مبلغ را به Breakdown و Lineage وصل کنید
کاربر یک Total میبیند، اما Ledger ممکن است principal، fee، tax، discount، reserve و adjustment داشته باشد. Test باید از Quote/Order تا Posting و Statement نشان دهد هر جزء از کدام Rule و Source آمده است. Total درست با breakdown غلط میتواند گزارش، refund یا revenue recognition را بشکند.
| جزء | Source | Version | Amount/Currency | Posting destination | Evidence |
|---|---|---|---|---|---|
| Principal | Order line | O-v3 | Fixture value | Receivable/Payable contract | source ID |
| Fee | Fee rule | F-v7 | Fixture value | Fee account | calculation trace |
| Tax | Approved rule | T-v2 | Fixture value | Tax account | owner-approved oracle |
| Discount | Promotion | P-v4 | Fixture value | Contra/reduction account | eligibility trace |
| Adjustment | Manual workflow | A-v1 | Fixture value | Adjustment account | role/reason/approval |
Transaction State Machine را از Ledger جدا کنید
State برای Workflow و تجربه کاربر است؛ Posting برای اثر مالی. این دو مرتبطاند اما یکی نیستند. یک State ممکن است قبل از Settlement تغییر کند یا یک correction با Posting جبرانی انجام شود بدون اینکه History پاک شود. مدل دقیق باید از Contract محصول بیاید.
Payment State Contract — illustrative only Intent: created → requires_action → authorized/failed/expired Funds flow: authorized → captured → settled Corrections: captured/settled → partially_refunded/refunded Exceptions: disputed/reversed/chargeback-like states per product For every transition: - allowed source and destination - actor and authorization - significant transaction data fingerprint - effective/posting/received time - side effects and postings - retry/idempotency behavior - notification and audit - terminal/reopen policy - reconciliation expectation
- Step skipping: Created مستقیماً به Settled نرود مگر Contract چنین مسیر قانونی داشته باشد.
- Terminal transition: Failed یا Refunded بیدلیل دوباره Active نشود.
- Out-of-order: Version قدیمی Current state را عقب نبرد.
- Concurrent actions: Capture و Cancel همزمان Invariant را نشکنند.
- Partial action: مجموع refundها از مقدار مجاز مصوب عبور نکند.
- Reopen/correction: History immutable و reason/actor ثبت شود.
- UI wording: «پرداخت شد» Stage واقعی و محدودیت آن را گمراهکننده نمایش ندهد.
Idempotency: Retry باید اثر کسبوکاری دوم نسازد
Retry در شبکه توزیعشده عادی است. پاسخ ممکن است پس از Commit گم شود؛ Client نمیداند درخواست اعمال شده یا نه. RFC ۹۱۱۰ معنای روش Idempotent در HTTP را تعریف میکند و هشدار میدهد روش non-idempotent بدون دانستن semantics نباید خودکار retry شود. برای Payment POST، تیم معمولاً به Contract کاربردی idempotency نیاز دارد؛ نام Header بهتنهایی کافی نیست.
Idempotency Contract Key owner: client/server/partner Scope: tenant + operation + business entity Request fingerprint: significant immutable fields First response: status/body/reference/error class Replay response: same business result, documented transport response Conflict: same key + different fingerprint = explicit reject Persistence point: before external/financial side effect Concurrency: one winner; others wait/read/reject deterministically Expiry: based on maximum replay horizon and policy Failure recovery: pending/unknown state with reconciliation Audit/metrics: first/replay/conflict/expired/unknown counts
| Scenario | تزریق | Invariant |
|---|---|---|
| Double click | دو request نزدیک | یک business effect و پاسخهای قابل توضیح |
| Timeout after commit | پاسخ اول حذف میشود | retry reference همان نتیجه را میدهد |
| Concurrent same key | دو worker همزمان | race دو Posting نسازد |
| Same key, changed amount | fingerprint متفاوت | Conflict صریح؛ مقدار اول یا دوم خاموش پذیرفته نشود |
| Expired key | replay بعد از TTL | Policy روشن؛ duplicate historical کشف شود |
| Downstream committed | provider success، local crash | reconciliation و recovery بدون double capture |
Exactly Once شعار است؛ اثر مالی یکتا را طراحی و تست کنید
Message broker ممکن است at-least-once delivery داشته باشد و همان Event چند بار برسد. حتی اگر Broker ویژگی خاصی ارائه دهد، side effect بیرونی، Database و Partner ممکن است در همان atomic boundary نباشند. هدف آزمون بهتر این است: «برای Business Event یکتا، اثر مالی مجاز طبق Contract بیش از یک بار ایجاد نمیشود و duplicate قابل مشاهده است.»
- Event ID یا business key در boundary مناسب unique است.
- Inbox/deduplication پیش از side effect یا در atomic unit مناسب ثبت میشود.
- Outbox انتشار Event را با transaction محلی هماهنگ میکند.
- Consumer پس از crash قبل/بعد از ack دوباره اجرا میشود.
- Poison message silent drop نمیشود و quarantine/owner دارد.
- Duplicate arrival در Audit دیده میشود اما Posting دوم نمیسازد.
- Retention dedupe از replay horizon واقعی کوتاهتر نیست، مگر risk پذیرفته شده باشد.
Double-entry Ledger: تراز شرط لازم است، نه کافی
در مدل ثبت دوطرفه، هر Transaction group باید مجموع Debit و Credit برابر داشته باشد؛ بااینحال دو Account اشتباه نیز میتوانند کاملاً تراز باشند. Test باید تراز، Account mapping، Currency، effective time، source event، posting type، reversal link و immutability را همزمان بررسی کند.
Ledger Posting Contract transactionGroupId: unique accounting unit postingId: immutable unique row/event sourceEventId: business provenance accountId/type: chart version + permitted direction amountUnits: non-negative canonical magnitude currency: explicit and account-compatible side: debit | credit effectiveAt: economic time postedAt: ledger time periodId: open/closed policy reversalOf: original posting/group reference metadata: minimal, versioned, auditable Core invariant per group and currency: Σ debit(amountUnits) = Σ credit(amountUnits)
| Invariant | Failure نمونه | تست |
|---|---|---|
| Balanced group | یک Posting گم شده | property query و commit rejection |
| Account mapping | Fee به principal account رفته | Rule-version fixture |
| Currency consistency | IRR و currency دیگر در یک balance جمع شده | negative schema/domain test |
| Immutability | UPDATE رکورد تاریخی را عوض میکند | permission/API/database audit test |
| Reversal linkage | منفی خام بدون مرجع ساخته شده | original-reference and amount policy |
| No orphan | Posting بدون source/group | referential/reconciliation scan |
| Period policy | تغییر مخفی در دوره بسته | close/reopen/adjustment workflow |
Balance را از Posting مشتق و Cache را قابل بازسازی کنید
Balance ذخیرهشده برای سرعت مفید است، اما باید Contract روشن داشته باشد: آیا authoritative است یا projection؟ اگر projection است، از Postingها قابل rebuild باشد و اختلاف آن با منبع حقیقت monitor شود. Update همزمان balance بدون کنترل میتواند Lost Update یا overspend بسازد.
- Opening balance + all in-scope postings = closing balance.
- Available، pending، held و settled balance بهوضوح جدا هستند.
- Projection rebuild از checkpoint و از ابتدا نتیجه یکسان میدهد.
- Duplicate event balance را تغییر نمیدهد.
- Reversal history را پاک نمیکند و effect خالص مطابق Policy است.
- Concurrent debitها از limit/available invariant عبور نمیکنند.
- Cache stale با version/freshness آشکار میشود، نه عدد قطعی ظاهری.
Concurrency و Isolation را با Business Invariant بسنجید
تست تککاربره race را نشان نمیدهد. دو برداشت، دو refund، close همزمان با Posting یا دو تخصیص اعتبار ممکن است جداگانه معتبر و در ترکیب نامعتبر باشند. Isolation level نامی، جای آزمون workload و invariant نیست.
مستندات جاری PostgreSQL درباره Transaction Isolation تفاوت Read Committed، Repeatable Read و Serializable و امکان Serialization failure را توضیح میدهد. این منبع فقط نمونهای برای محصولی است که PostgreSQL دارد؛ رفتار DB، ORM و retry implementation واقعی خود را مستند و آزمایش کنید.
| Race | Schedule آزمون | Invariant |
|---|---|---|
| Double spend | دو debit پس از خواندن یک available balance | حد مجاز نقض نشود یا نتیجه policy-defined باشد |
| Refund race | دو refund جزئی همزمان | مجموع از refundable amount عبور نکند |
| Close vs post | period close و late posting | یا قبل close ثبت یا به adjustment path هدایت شود |
| Idempotency race | دو request با یک key | یک side effect |
| Limit update | limit و transaction همزمان | version/authorization policy رعایت شود |
| Serializable retry | conflict عمدی | کل transaction logic retry؛ duplicate external effect ممنوع |
Retry فقط SQL statement آخر میتواند تصمیمهای قبلی را با snapshot جدید ناسازگار کند. Side effect بیرونی را نیز داخل retry loop خام قرار ندهید. Fault schedule، barrier و correlation ID لازماند تا race deterministic و نتیجه قابل تکرار شود.
Authorization تراکنش را از Login جدا کنید
Authenticated بودن کاربر لزوماً به معنای مجازبودن همان Transaction نیست. Actor، account، amount، beneficiary، currency، purpose، limit، device/risk context و separation of duties میتوانند در تصمیم دخیل باشند. Significant data که کاربر تأیید میکند نباید پس از authorization تغییر کند.
راهنمای رسمی OWASP برای Transaction Authorization بر پیوند authorization با داده مهم تراکنش، اجرای server-side، جلوگیری از تغییر پس از تأیید و کنترل ترتیب Stateها تأکید دارد. این Cheat Sheet راهنمای امنیتی است، نه قانون، تضمین یا نسخه یکسان برای همه محصولات.
- What-you-confirm با beneficiary/amount/currency واقعی server-side تطبیق دارد.
- تغییر هر field مهم، challenge/approval قبلی را invalidate میکند.
- Authorization token به Transaction و زمان محدود bind است.
- Stepها با URL یا API مستقیم skip نمیشوند.
- روش ضعیفتر با parameter tampering انتخاب نمیشود.
- Maker نمیتواند Checker خود باشد، اگر Policy جداسازی نقش دارد.
- Limit و policy در execution دوباره enforce میشوند، نه فقط UI.
- Failure، retry و expiry اطلاعات کافی برای حمله یا replay نمیدهند.
برای Threat model، authorization عمومی، secret/session و تست نفوذ، به راهنمای تست امنیت نرمافزار ارجاع دهید. این صفحه منطق مالی را مالک است، نه کل AppSec.
Settlement را با Payment Status یکی نگیرید
ممکن است محصول Transaction را Captured نشان دهد، Partner آن را در Batch بگذارد و Bank statement بعداً مبلغ خالصی با fee/adjustment ثبت کند. هر Stage منبع، زمان، ID و Finality متفاوت دارد. «موفق» باید مشخص کند از نگاه چه System و چه مرحلهای است.
| Stage | Source evidence | ID | Time | Failure/unknown |
|---|---|---|---|---|
| Intent | محصول | payment/order ID | created | client abandoned |
| Authorization | provider response | provider reference | authorized | timeout/unknown |
| Capture | capture record | capture ID | captured | duplicate/partial |
| Settlement batch | partner file/API | batch/line ID | settlement date | missing/rejected |
| Bank/financial statement | external statement | statement reference | value date | net fee/adjustment |
| Internal ledger | posting group | group/event ID | effective/posted | mapping/period error |
Reconciliation یک Query آخر روز نیست؛ Control است
Reconciliation باید Completeness و Correctness را بین منابع مستقل بررسی کند، اختلاف را طبقهبندی کند و تا resolution قابل پیگیری نگه دارد. صرف اینکه Sum کل دو سیستم برابر است کافی نیست؛ دو missing و دو duplicate میتوانند یکدیگر را خنثی کنند.
Reconciliation Contract Source A/B: authoritative scope and snapshot IDs Population: date/cut-off/currency/entity/state Matching keys: stable IDs + fallback hierarchy Amount comparison: gross/net/fee/tax/currency/tolerance policy State comparison: explicit mapping/version Completeness: A-only, B-only, duplicate, rejected Timing window: expected lag and aging buckets Exceptions: reason, owner, SLA, evidence, disposition Adjustments: approval and posting linkage Rerun: immutable inputs + run ID + reproducible result Closure: zero/unresolved accepted by authorized owner
| Mismatch | معنای ممکن | اقدام تست/عملیات |
|---|---|---|
| A-only | delay، loss، wrong cut-off یا false record | trace source→transport→destination |
| B-only | unknown ingestion، duplicate key یا missing source | provenance و ownership |
| Amount | fee، rounding، partial، currency یا mapping | breakdown compare؛ no blind tolerance |
| State | lag، stale event یا mapping error | version/timeline compare |
| Duplicate | retry یا file re-import | idempotency and unique controls |
| Unmatched aging | مسئله ماندگار و exposure | escalation، reserve/hold طبق policy |
| Offsetting totals | خطاهای متقابل پنهان | row/event-level matching |
Refund، Reversal، Void و Dispute را یکی نکنید
واژهها و lifecycle دقیق به محصول و Partner بستگی دارند. Test Plan باید تفاوت Business event، زمان مجاز، مقدار قابل انجام، fee، authorization، Posting، notification و settlement را نسخهدار کند. استفاده از amount منفی برای همه مسیرها History و گزارش را مبهم میکند.
| Scenario | پرسش | Invariant نمونه |
|---|---|---|
| Full refund | کدام مقدار و fee برمیگردد؟ | effect طبق policy و original link |
| Partial refunds | ترتیب و مجموع چگونه است؟ | cumulative ≤ refundable amount مصوب |
| Concurrent refund | دو agent همزمان | race over-refund نسازد |
| Refund retry | timeout بعد از provider commit | یک refund business effect |
| Reversal/correction | اصل حذف یا جبران میشود؟ | immutable original + linked corrective entries |
| Dispute-like event | چه کسی و در کدام state اقدام میکند؟ | separate workflow، evidence و posting mapping |
| Closed period | اصلاح تاریخی چگونه ثبت میشود؟ | current-period adjustment طبق policy |
Fee، Tax، Discount و FX را Version کنید
قانون مالی در Code تنها زندگی نمیکند؛ Configuration، جدول، قرارداد Partner و تاریخ اثر آن را تغییر میدهند. هر محاسبه باید Rule ID/version، effective window، source و breakdown داشته باشد تا Test تاریخی و بازتولید گزارش ممکن شود.
- مرز قبل/در/بعد از Effective time و timezone؛
- Ruleهای overlap و gap؛
- Minimum/maximum، tier و cumulative threshold؛
- Discount stacking و ترتیب اعمال؛
- Fee refundability و partial allocation؛
- FX pair direction، source timestamp، stale/missing rate و rounding؛
- Retroactive correction بدون بازنویسی silent historical output؛
- Fallback فقط اگر Owner صریحاً تصویب کرده باشد.
Time، Cut-off و Period Close را مدل کنید
CreatedAt، AuthorizedAt، EffectiveAt، PostedAt، SettledAt و ValueDate یک timestamp نیستند. گزارش روزانه نیز به timezone، تعطیلی، cut-off و late event وابسته است. Timestamp را با رشته محلی یا تاریخ شمسی منبع حقیقت نکنید؛ نمایش را از instant canonical و business date جدا نگه دارید.
| Case | تزریق | بررسی |
|---|---|---|
| مرز روز | یک ثانیه قبل/بعد cut-off | business date/period طبق policy |
| Late event | event قدیمی امروز دریافت میشود | effective vs received time |
| Clock skew | Partner جلو/عقب | ترتیب به timestamp نامطمئن واگذار نشود |
| Closed period | correction پس از close | adjustment/reopen authorization |
| Tehran display | UTC→Asia/Tehran→شمسی | نمایش درست، instant/provenance حفظ |
| Batch rerun | job دوباره در همان/روز بعد | run identity و idempotency |
Audit Trail باید پاسخگو باشد، نه مخزن راز
Audit خوب نشان میدهد چه Actor یا Service، کدام Business entity را، با چه Action، version، reason، authorization و outcome تغییر داده است. Log نباید PAN، credential، OTP، token، secret یا payload حساس کامل را برای راحتی Debug ذخیره کند. Access، retention و integrity آن نیز کنترل میخواهند.
- Actor، service identity و impersonation context؛
- Business entity/event/reference، نه فقط URL؛
- قبل/بعد بهشکل حداقل و مجاز یا version reference؛
- Reason و approval برای manual adjustment؛
- Correlation از request تا provider، ledger و reconciliation؛
- UTC instant، timezone context و clock source؛
- Success/failure/unknown و error class بدون secret؛
- Tamper detection، access review و export control؛
- قابلیت جستوجو بدون افشای داده شخصی نامرتبط.
PCI DSS و Compliance را با Applicability آغاز کنید
نام Industry یا وجود Button پرداخت بهتنهایی Scope هیچ استاندارد یا قانون را تعیین نمیکند. Data flow، نقش سازمان، قرارداد، بازار، نوع Account Data، outsourcing، network segmentation و اثر سیستم بر محیط مشمول باید توسط نقشهای حقوقی، امنیتی و Compliance ارزیابی شوند. QA تصمیم را به Control و Evidence تبدیل میکند.
کتابخانه رسمی PCI Security Standards Council در زمان این بازبینی PCI DSS v4.۰.۱ و اسناد پشتیبان آن را فهرست میکند. این واقعیت به معنای مشمولبودن خودکار هر اپ مالی، ایرانی یا غیرایرانی نیست. نسخه، Scope، SAQ/assessment path و مسئول ارزیابی باید برای سازمان مشخص شود.
| فیلد Applicability | پرسش | خروجی QA پس از تصمیم |
|---|---|---|
| Authority/source | قانون، استاندارد، قرارداد یا policy کدام است؟ | نسخه و Requirement ID |
| Entity/role | سازمان چه نقش تعریفشدهای دارد؟ | Scope actor/system |
| Data/process | چه دادهای کجا store/process/transmit یا affect میشود؟ | data-flow test points |
| Environment | Boundary و segmentation مصوب چیست؟ | control validation scope |
| Third party | مسئولیت shared چگونه تقسیم شده؟ | evidence request و interface tests |
| Effective date | کدام نسخه در چه زمانی لازم است؟ | time-bound regression plan |
| Exception | چه استثنا/compensating process تأیید شده؟ | testable control + expiry |
برای روش تبدیل منبع نسخهدار به Requirement و Evidence از راهنمای تست انطباق و برای GDPR/CCPA/HIPAA و داده شخصی از راهنمای تست حریم خصوصی استفاده کنید. هیچ Test Suite بهتنهایی Compliance کل سازمان را certify نمیکند.
داده تست مالی: Synthetic-first و بدون راز
داده مالی واقعی فقط مبلغ نیست؛ Account، PAN، نام، شناسه، device، IP، description، beneficiary، token، statement و log میتوانند حساس باشند. Fixture مصنوعی باید از فرمتهای رزروشده و واضح استفاده کند و هرگز قابلیت Route شدن به Provider یا حساب واقعی نداشته باشد.
- Account/merchant/customer/payment IDهای `SYN-*` و namespace جدا؛
- هیچ PAN، CVV2، PIN، OTP، credential، token یا کلید واقعی؛
- Provider/Bank/PSP stub محلی و denylist/allowlist شبکه؛
- Currency و Amountهای خیالی اما Contract-valid؛
- Generator version، seed، checksum و expected postings؛
- Edge catalog برای zero، max، negative-disallowed، boundary و overflow؛
- Retention، access، export و destruction محیط تست؛
- Log/screenshot/ticket redaction و secret scanning؛
- Production-like distribution فقط با synthetic generation یا تصمیم رسمی داده.
Property-based و Model-based Testing برای پول
Example مشخص لازم است، اما فضای State، Currency، retry و concurrency بزرگ است. Property-based testing ورودیهای متعدد میسازد و invariant را میسنجد؛ Model-based testing مسیرهای مجاز State را تولید میکند. هر دو به Oracle و Generator سالم و shrinking قابل توضیح نیاز دارند.
| Property | Generator | Counterexample مورد انتظار |
|---|---|---|
| Debit=Credit per group/currency | event و posting breakdown | گروه/ردیف حداقل نامتوازن |
| Duplicate no extra effect | sequence با replay | کوتاهترین sequence با effect دوم |
| State monotonic by version | permutation نسخهها | late event که Current را عقب برد |
| Refund bound | چند refund جزئی/concurrent | کمترین مجموع عبورکننده |
| Projection rebuild equality | posting history/checkpoint | نخستین event اختلافساز |
| Allocation conservation | total و N destination | residual گم یا اضافه |
| Reconciliation completeness | missing/duplicate/mismatch pairs | موردی که aggregate پنهان کرد |
Propertyها قانون مالی عمومی نیستند. مثلاً «refund کامل همیشه net zero است» ممکن است با fee غیرقابلبرگشت یا Policy دیگر غلط باشد. هر invariant باید منبع، Scope و Version داشته باشد.
Migration و Backfill را با Trial Balance تمام نکنید
تراز کل قبل و بعد میتواند با Account mapping غلط، Currency مخلوط، lost lineage، duplicate event یا تاریخ اثر اشتباه همراه باشد. Migration باید population، mapping version، reject manifest، source snapshot، delta/cutover، semantic invariants، reproducibility و rollback/reconcile داشته باشد.
| کنترل | معیار ضعیف | معیار بهتر |
|---|---|---|
| Completeness | row count کل | entity/state/currency/period slices + rejects |
| Balance | grand total | account/currency/period/customer aggregate + event sample |
| Lineage | مقدار منتقل شد | source ID→target posting/event mapping |
| Semantics | field non-null | state/account/rule/time meaning preserved |
| History | current balance درست | original/correction/reversal chain intact |
| Cutover | job succeeded | snapshot + delta + no gap/double-write |
| Rollback | backup exists | restore rehearsed + post-rollback reconciliation |
Performance: Throughput بدون Correctness خطرناک است
سیستم میتواند هزار Request در ثانیه پاسخ دهد اما duplicate Posting، backlog نامرئی یا stale balance بسازد. Workload مالی باید mix واقعی Stateها، retry، hot account، batch، close، statement import و reconciliation را مدل کند و پس از Load، invariantها را دوباره بررسی کند.
| بعد | Metric | Correctness companion |
|---|---|---|
| Latency | p50/p95/p99 per operation | result state و timeout-after-commit outcome |
| Throughput | accepted/completed per second | unique business effects |
| Queue | depth/oldest age | no lost/duplicate events |
| Ledger | posting rate | balanced groups و projection lag |
| Reconciliation | run duration | population completeness و deterministic result |
| Recovery | catch-up time | post-recovery balance/state/reconcile |
| Hot key | lock/conflict/retry rate | limit/refund invariant |
طراحی مدل بار، percentile، saturation و معیار پذیرش در راهنمای برنامه تست عملکرد آمده است. Threshold را از SLA، ظرفیت، Risk و فرآیند واقعی بگیرید؛ عدد عمومی این مقاله Oracle نیست.
Failure Injection: Unknown مالی را آشکار کنید
مهمترین حالت همیشه Success یا Fail نیست؛ «نمیدانیم اعمال شد یا نه» است. قطع بین Commit و Response، crash بین Provider و Ledger، فایل ناقص، clock skew، disk full، delayed webhook و serialization failure را در محیط مجاز تزریق کنید.
| Fault point | Unknown | Recovery invariant |
|---|---|---|
| قبل local commit | درخواست پذیرفته شد؟ | no financial effect یا retry safe |
| بعد local commit/قبل response | Client نتیجه را نمیداند | idempotent lookup/retry |
| بعد provider/قبل local record | external success، internal unknown | reconciliation repairs once |
| Outbox publish gap | ledger ثبت، event غایب | republish without double effect |
| Consumer before ack | event دوباره میرسد | dedupe/no second posting |
| Partial statement file | population ناقص | atomic/manifest validation؛ no false closure |
| Serialization failure | transaction aborted | whole logic retry without external replay |
راهنمای تست تابآوری نحوه تعریف steady state، fault boundary، recovery evidence و stop condition را پوشش میدهد. Fault injection در Production مالی بدون مجوز، blast radius، guardrail و rollback اقدامی پرخطر است و از متن مقاله مجوز نمیگیرد.
Backup، Restore و Reconciliation پس از بازیابی
Backup موفق ثابت نمیکند Ledger قابل بازیابی یا Statementها کاملاند. Restore را در محیط پاک با schema/config/key سازگار اجرا کنید، posting invariants و balances را بسنجید، سپس Eventها و Partner changes بعد از Recovery Point را reconcile کنید.
- Manifest دقیق database، object store، config، rule و key dependency؛
- Checksum و قابلیت decrypt با access کنترلشده؛
- Restore time و recovery point مشاهدهشده، نه فقط هدف قراردادی؛
- Debit=Credit و no orphan/currency mismatch پس از Restore؛
- Projection rebuild و balance comparison؛
- Gap event replay با idempotency؛
- Partner/statement reconciliation پس از بازگشت؛
- Return-to-service gate، monitoring و owner؛
- Action item و effectiveness retest.
Observability: Metric را به پول و Control وصل کنید
CPU و Error rate سبز میتوانند کنار duplicate payment و stale settlement باشند. Signalهای مالی باید numerator، denominator، currency، stage، source، time window، late-arrival policy، slice و owner داشته باشند. مبلغ Aggregate بدون Count و Distribution نیز میتواند مسئله را پنهان کند.
| Signal | Counter-signal | Slice | Runbook |
|---|---|---|---|
| Requests accepted | unique effects/completed/unknown | operation/partner | idempotency trace |
| Provider success | local posting/reconcile status | provider/currency | repair/hold |
| Ledger balanced | account mapping/recon mismatch | account/currency/period | stop posting/escalate |
| Reconcile difference | count/amount/aging | reason/partner/day | owner/SLA |
| Refund success | cumulative/refundable amount | channel/reason | over-refund containment |
| Queue depth | oldest age/business exposure | event type | scale/replay/reconcile |
Metric label نباید account/card/customer ID با cardinality و Privacy risk بالا حمل کند. Correlation ID کنترلشده، access محدود، retention و redaction را با Security/Privacy تعیین کنید.
Release Identity شامل Rule و Config است
Fee table، account mapping، limit، routing، currency enablement، cut-off و Feature Flag میتوانند بدون Code deploy اثر مالی را تغییر دهند. Release Evidence باید کل Configuration مؤثر را pin کند.
Financial Release Identity Code/image digest: ___ Database schema/migration:___ Money/currency contract: ___ Accounting/chart mapping: ___ Fee/tax/discount rules: ___ State machine version: ___ Partner/API mapping: ___ Idempotency/TTL policy: ___ Limits/authorization: ___ Cut-off/calendar/timezone:___ Feature flags/routing: ___ Fixture/generator seed: ___ Evidence pack/checksum: ___
Release Gate را بر Invariant و Exposure بنا کنید
صد UI test سبز یک Idempotency race اجرانشده را خنثی نمیکند. Gate باید critical money invariants، reconciliation، migration، failure/recovery، Security/Compliance references، blocked evidence و rollback را جدا گزارش کند.
Financial Release Memo Scope and release identity: ___ Money/state/ledger contracts: ___ Critical invariants and evidence: ___ Provider/settlement/reconciliation:___ Concurrency/failure/recovery: ___ Security/privacy/compliance refs: ___ Open defects and exposure: ___ Blocked/inconclusive/not-run: ___ Rollback/repair/runbooks: ___ Monitoring and owners: ___ Residual risk: ___ Recommendation: Go / Conditional / Hold Authorized decision and expiry: ___
| Evidence status | معنا | رفتار Gate |
|---|---|---|
| Pass | در Scope/Oracle مشاهدهشده مطابق بود | limitation حفظ شود |
| Fail | Invariant یا requirement نقض شد | fix/contain/authorized decision |
| Blocked | تست اجرا نشد | Unknown، نه Pass |
| Inconclusive | Oracle/evidence کافی نیست | Exposure و investigation |
| Stale | به Rule/config قبلی مربوط است | impact analysis/retest |
| Reconciled exception | اختلاف توضیح و حل شده | evidence و approval لازم |
| Accepted exception | اختلاف باز با تصمیم محدود | owner/limit/expiry/monitor |
آزمایش بازتولیدپذیر: Delivery count در برابر Ledger
برای نشاندادن مکانیک duplicate و stale state، یک Fixture کاملاً ساختگی اجرا شد. هیچ کاربر، حساب، کارت، PSP، بانک، Merchant، سفارش یا پول واقعی وجود ندارد. Currency برچسب IRR دارد، اما Amountها صرفاً اعداد آموزشیاند و هیچ نرخ، fee، tax یا قاعده حسابداری واقعی را نمایش نمیدهند.
Fixture: fictional-payment-ledger-v1 Payment: PAY-SYN-100 Events delivered: 6 1 INIT v1 pending, effect 0 2 CAP v2 captured, effect +120000 units 3 duplicate CAP v2, effect +120000 units 4 REF v3 partially_refunded, effect -20000 units 5 duplicate REF v3, effect -20000 units 6 STATUS-OLD v1 pending, effect 0 Fictional independent statement net: 100000 IRR units
مصرفکننده ساده هر Delivery را اثر تازه و آخرین Arrival را State جاری میگیرد. مصرفکننده قراردادی Event ID را یکبار اعمال میکند، State را فقط با Version بزرگتر جلو میبرد و برای Capture/Refund دو Posting متقارن آموزشی میسازد.
| روش | Net units | Current state | Duplicate/Stale | Ledger | Statement difference |
|---|---|---|---|---|---|
| Naive delivery sum | 200000 | pending v1 | همه اعمال شدند | ندارد | +100000 |
| Contract-aware | 100000 | partially_refunded v3 | ۲ duplicate، ۱ stale کنار گذاشته | ۴ posting؛ Debit=Credit=۱۴۰۰۰۰ | 0 |
{
"fixture": "fictional-payment-ledger-v1",
"deliveries": 6,
"naive": {
"netUnits": 200000,
"state": "pending",
"statementDifferenceUnits": 100000
},
"contractAware": {
"uniqueNetUnits": 100000,
"currentState": "partially_refunded",
"currentStateVersion": 3,
"duplicatesIgnored": 2,
"staleStatesIgnored": 1,
"postings": 4,
"debitUnits": 140000,
"creditUnits": 140000,
"balanced": true,
"statementDifferenceUnits": 0
}
}
محدودیتها اساسیاند: Fixture عمداً برای تولید Failure ساخته شده، فقط شش Event دارد و هیچ concurrency واقعی، provider، database، security، accounting policy، FX، fee، tax، close، fraud، availability یا رفتار انسانی را نمیسنجد. تراز آموزشی Accountهای خیالی، صحت حسابداری یا ایمنی مالی هیچ محصولی را ثابت نمیکند. اعداد Probability، benchmark، خسارت، حجم، threshold یا نمونه آماری نیستند.
درس محدود این است: Delivery count و آخرین timestamp باید با Event identity، Version rule، Posting invariant و منبع مستقل Reconciliation تکمیل شوند. Account mapping و Contract واقعی را Finance/Accounting/Product محصول تعیین میکنند.
سناریوی بومی: پرداخت آزمایشی IRR بدون اتصال واقعی
یک فروشگاه فرضی فارسی را در محیط کاملاً جدا تصور کنید. Order و Payment ID با `SYN-` آغاز میشوند، Provider یک Stub محلی است، هیچ IPG/PSP/بانک واقعی فراخوانی نمیشود و هیچ PAN، CVV2، PIN، OTP، حساب، موبایل، نام، کد ملی، Token یا پول واقعی وجود ندارد. این Lab درباره مقررات، شبکه پرداخت یا قواعد حسابداری ایران ادعایی ندارد.
| ریسک | تزریق ساختگی | Invariant | Evidence |
|---|---|---|---|
| ریال/تومان | API IRR، UI تومان با label | conversion صریح و round-trip canonical | API/UI/ledger compare |
| رقم و RTL | ۰۱۲/۰۱۲/۰۱۲ و mixed IDs | parse درست؛ ID و amount جابهجا نشوند | input/display/print checks |
| Double click | دو submit با یک key | یک payment effect | request/event/posting trace |
| Timeout after commit | Stub ثبت، response حذف | retry همان reference را بازیابی کند | idempotency/reconcile |
| Duplicate callback | CAP دو بار | یک Capture effect | dedupe metric + ledger |
| Late status | Pending v1 پس از Refund v3 | Current state عقب نرود | history/current query |
| Tehran/Jalali | UTC instant نزدیک cut-off | business date طبق policy؛ شمسی فقط نمایش | report/statement slice |
| Recovery | crash بین Stub و local posting | unknown آشکار و repair فقط یک بار | runbook + reconciliation |
Dependencyها در cache داخلی، Artifactها با checksum و Fixture با seed ذخیره میشوند. Egress محیط به مقصدهای واقعی deny است. اگر تیم بعدها Sandbox بیرونی اضافه کند، Scope داده، credential، Terms، rate limit، webhook verification، cleanup و تفاوت Sandbox/Production باید دوباره بررسی شود.
نقشها و Decision Rights
| نقش | مسئولیت نمونه | نباید بهتنهایی تعیین کند |
|---|---|---|
| Finance/Accounting | policy، chart، posting oracle، close | implementation correctness بدون Evidence |
| Product/Payments | journey، state semantics، partner contract | Accounting/Compliance applicability |
| Engineering | design، atomicity، idempotency، observability | پذیرش مستقل residual risk |
| QA/Test | risk scenarios، execution، limitations، evidence | تضمین پول/قانون یا ساخت Oracle مالی |
| Security/Fraud | threat، authorization، detection/control | صحت Ledger کامل |
| Legal/Compliance/Privacy | applicability و requirement approval | فنیبودن Control بدون test |
| Operations/SRE | deploy، monitoring، recovery، runbook | manual adjustment بدون approval |
| Authorized risk owner | release/residual risk decision | تغییر evidence یا requirement history |
برنامه ۳۰روزه Pilot
| هفته | خروجی | کار | معیار اتمام |
|---|---|---|---|
| ۱: Contract | Context، Money و State contracts | یک flow محدود، owner، currency، stages، source of truth | ابهام Success/Amount/Time رفع شده |
| ۲: Ledger | Posting/Idempotency/Reconcile contracts | event IDs، account mapping، duplicate/late/unknown cases | invariantها owner و version دارند |
| ۳: Evidence | Synthetic fixtures و deterministic tests | boundary، property، concurrency، failure، migration | failure قابل بازتولید و traceable است |
| ۴: Decision | Release memo، dashboard و runbooks | restore/reconcile exercise، unknown review، gate | residual risk/owner/expiry روشناند |
- کل بانک یا ERP را انتخاب نکنید؛ یک Capture→Refund→Reconcile flow کافی است.
- Finance/Accounting را پیش از Expected Result وارد کنید.
- یک Money Contract و یک State diagram نسخهدار بسازید.
- سه Invariant حیاتی: unique effect، balanced postings و reconciliation را اول اجرا کنید.
- حداقل timeout-after-commit و concurrent retry را تزریق کنید.
- گزارش را بر Exposure و Unknown بنا کنید، نه تعداد Test Case.
ضدالگوهای رایج تست نرمافزار مالی
| ضدالگو | مسئله | اصلاح |
|---|---|---|
| Success page = paid | Stage و source مبهم است | state/ledger/provider/reconcile evidence |
| amount بدون currency | واحد و scale حدس زده میشود | Money Contract |
| Float everywhere | precision/rounding کنترلنشده | approved representation and oracle |
| Retry POST blindly | effect دوم ممکن است | idempotency contract |
| Exactly once label | boundary بیرونی نادیده گرفته میشود | unique business effect + reconcile |
| Debit=Credit = correct | mapping غلط نیز تراز است | account/source/currency checks |
| Balance mutable | history و race پنهان | immutable postings + rebuildable projection |
| Last arrival wins | late event State را عقب میبرد | version/state contract |
| Sum reconciliation | offsetting errors گم میشوند | event-level matching |
| Blind tolerance | مغایرت واقعی پذیرفته میشود | reason-specific policy |
| Production financial data in test | privacy/security exposure | synthetic-first isolation |
| PCI for every fintech | Applicability حدس زده میشود | formal scope decision |
| Automation guarantees accuracy | oracle/common-code defect پنهان | independent model/review/properties |
| Load test without ledger check | سریع اما غلط | post-load invariants |
| Backup green = recoverable | restore/reconcile آزموده نشده | clean recovery exercise |
| Blocked hidden as skipped | unknown exposure حذف میشود | explicit gate status |
چکلیست نهایی تست نرمافزار مالی
- System of Record، economic event و Stageهای Success مشخصاند.
- Amount، Currency، Scale، Display، Sign، Precision و Rounding قرارداد دارند.
- Breakdown هر Total به Source، Rule version و Posting متصل است.
- State Machine، Actor، version و transitionهای ممنوع تعریف شدهاند.
- Idempotency scope، key، fingerprint، conflict، expiry و recovery روشناند.
- Duplicate، retry، timeout-after-commit و out-of-order آزموده شدهاند.
- Ledger groups به تفکیک Currency ترازند و Account mapping بررسی شده است.
- Postingها immutable و correction/reversal linked هستند.
- Balance projection از Postingها قابل rebuild و مقایسه است.
- Concurrency برای debit/refund/close/idempotency با schedule کنترلشده اجرا شده است.
- Transaction authorization به داده مهم و execution server-side وصل است.
- Settlement stages، IDs، timestampها و source evidence جدا هستند.
- Reconciliation event-level، complete، rerunnable و exception-owned است.
- Refund/void/reversal/dispute و fee behavior از هم جدا شدهاند.
- Rule، Rate، Calendar، Cut-off و Timezone نسخهدارند.
- Audit traceable است و secret/card/account data نامجاز نگه نمیدارد.
- PCI/Privacy/AML/KYC/Reporting فقط طبق Applicability مصوب وارد Plan شدهاند.
- Fixtureها مصنوعی، isolated، seeded و غیرقابل Route به دنیای واقعیاند.
- Migration بهجز Count و Total، lineage/semantics/history را میسنجد.
- Load test با unique effects، balance و reconciliation همراه است.
- Failure، Restore و Reconciliation پس از recovery تمرین شدهاند.
- Release identity شامل Code، Schema، Rule، Mapping، Flag و Config است.
- Blocked/Inconclusive/Stale بهعنوان Unknown در Gate دیده میشوند.
- Monitoring به business effect و exposure وصل است، نه فقط زیرساخت.
سؤالات متداول تست نرمافزار مالی
مهمترین Invariant برای تست پرداخت چیست؟
یک پاسخ جهانی وجود ندارد، اما «هر Business Event یکتا حداکثر اثر مالی مجاز خود را ایجاد کند» معمولاً نقطه شروع مفیدی است. این Invariant باید با Money/State/Ledger contract، duplicate و timeout tests و Reconciliation تکمیل شود. Finance/Product باید معنای اثر مجاز را تصویب کنند.
آیا تراز بودن Debit و Credit یعنی دفترکل درست است؟
خیر. دو Posting با Account، Currency، مبلغ، زمان یا Source اشتباه نیز میتوانند تراز باشند. علاوه بر Debit=Credit، mapping، provenance، immutability، period، reversal link، balance projection و reconciliation با منبع مستقل را تست کنید.
تفاوت Idempotency و Duplicate Detection چیست؟
Duplicate detection تشخیص میدهد Event یا Request قبلاً دیده شده؛ Idempotency قرارداد گستردهتری است که میگوید تکرار چگونه پاسخ میگیرد و چرا اثر کسبوکاری دوم نمیسازد. Scope، fingerprint، conflict، concurrency، persistence point، expiry و recovery بخشهای مهم آناند.
آیا همه نرمافزارهای مالی مشمول PCI DSS هستند؟
خیر؛ نام «مالی» یا داشتن مسیر پرداخت بهتنهایی تصمیم Scope نیست. نوع Account Data، store/process/transmit یا اثر بر محیط مربوط، نقش سازمان، معماری، outsourcing و اسناد جاری باید توسط مسئولان Compliance/Security تعیین شوند. QA Requirement مصوب و نسخهدار را تست میکند.
برای شروع تست مالی چه سناریویی مناسب است؟
یک flow مصنوعی محدود مانند Capture→partial refund→reconciliation انتخاب کنید. Money Contract، State versions، Event IDs و Posting oracle بسازید؛ سپس duplicate callback، timeout-after-commit، concurrent refund، stale event و اختلاف Statement را اجرا کنید. هیچ حساب، کارت، درگاه یا پول واقعی لازم نیست.
جمعبندی: صحت مالی از Contract تا Reconciliation ساخته میشود
تست نرمافزار مالی با محاسبه چند Example یا سبزشدن صفحه پرداخت تمام نمیشود. Money Contract معنا و واحد را تثبیت میکند؛ State Machine مسیر را محدود میسازد؛ Idempotency و concurrency از اثر تکراری جلوگیری میکنند؛ Ledger اثر مالی را traceable نگه میدارد؛ Settlement و Reconciliation اختلاف واقعیتهای مستقل را آشکار میکنند؛ و Failure/Restore/Monitoring نشان میدهند Control در شرایط دشوار چه میکند.
پرسش درست «چند تراکنش Pass شد؟» نیست. بپرسید: «برای این Currency، Rule و Release، کدام Business Event چه اثر یکتایی ساخت، چگونه در Ledger ثبت شد، با کدام Source مستقل reconcile شد، چه Unknownی باقی است و چه کسی ریسک آن را میپذیرد؟»
روش تدوین و منابع
این راهنما با بازبینی انتقادی نسخه قبلی، جداسازی intent صفحه از راهنمای عمومی FinTech/IPG/Security/Compliance و تطبیق ادعاهای فنی متغیر با منابع رسمی جاری تدوین شد. آزمایش ششرویدادی با Node.js ۲۴.۱۸.۰، بدون dependency و بهصورت deterministic اجرا شد؛ تمام داده و Accountها ساختگیاند.
- PCI Security Standards Council Document Library: PCI DSS v4.۰.۱ و اسناد پشتیبان جاری، فقط پس از Scope/Applicability.
- OWASP Transaction Authorization Cheat Sheet: significant transaction data، server-side enforcement و state-flow controls.
- RFC ۹۱۱۰ §۹.۲.۲: semantics روشهای Idempotent و محدودیت retry خودکار.
- PostgreSQL Current Documentation، Transaction Isolation: anomalies، isolation و retry؛ صرفاً مثال فناوری وابسته به محصول.
بازبینی محتوایی: مرداد ۱۴۰۵. Standard، Regulation، Partner contract، Currency rule، Accounting policy و API تغییر میکنند؛ نسخه جاری و Applicability محصول خود را پیش از استفاده تأیید کنید.

