{"id":1652,"date":"2026-09-23T15:45:42","date_gmt":"2026-09-23T14:45:42","guid":{"rendered":"https:\/\/ruby-doc.org\/learn\/?p=1652"},"modified":"2026-09-23T14:59:31","modified_gmt":"2026-09-23T13:59:31","slug":"build-a-payment-reconciliation-dashboard-with-ruby-on-rails","status":"publish","type":"post","link":"https:\/\/ruby-doc.org\/learn\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\/","title":{"rendered":"Build a Payment Reconciliation Dashboard with Ruby on Rails"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">The order says paid, and the payment provider shows a completed transaction. Yet the bank deposit is smaller than the invoice. Someone has to explain the difference.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This gives you a starting point for a Ruby on Rails project: a dashboard that brings together orders, payment records, fees, and settlement reports. Its first version can show which records have been reconciled and which require investigation, without moving funds.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For a developer who is working beyond the standard CRUD (create, read, update, delete) applications, this project presents some issues that are easily underestimated. These include importing the same report multiple times; comparing amounts that are based in different currencies; and determining what action to take when an expected payment is missing.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Choose the question your first release will answer<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Start with a small online store selling downloadable design templates. Customers place orders through its Rails application, while a separate provider collects the money. Your project will reconcile the records from those two systems.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Our merchant client wishes to understand which payments have successfully settled.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Build that screen first. Include an order total, a provider reference, an expected net amount, and a settlement status. Give records needing investigation their own filter.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Work with sample data in development. Once the matching rules work, replace the sample importer with an importer for the provider&#8217;s documented export or reporting API.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A checkout adapter could support <a href=\"https:\/\/cryptoprocessing.com\/\">crypto payment processing<\/a> if the selected provider is available to the merchant&#8217;s contracting entity and region.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Keep provider access in a small Ruby adapter. The rest of the application should operate against your own records rather than relying on every field name in an external response.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Represent the records you need to explain<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">To make things easier at first glance, you might think about placing everything in your Order model. However, when a customer attempts to pay again for an Order or when a settlement includes multiple orders, that would cause problems.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">One possible way to represent these items initially is:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><th>Record<\/th><th>What it represents<\/th><\/tr><tr><td>Order<\/td><td>The customer&#8217;s purchase, including price and currency<\/td><\/tr><tr><td>PaymentAttempt<\/td><td>One attempt to pay, with a provider reference and outcome<\/td><\/tr><tr><td>PaymentEvent<\/td><td>A provider notification and its processing status<\/td><\/tr><tr><td>SettlementEntry<\/td><td>A settlement report entry containing amounts, fees, and references<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">You create these models and decide their behaviour; Rails does not supply a predefined payment reconciliation system.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Keep imported settlement entries even when they cannot yet be reconciled. An unmatched entry is evidence of a problem to investigate. Removing it because no associated order was found would hide that problem.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Save both the import batch and source row reference. When you want to determine why an amount doesn&#8217;t match, the dashboard should allow users to navigate back to the report used as input.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Determine what each amount represents<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">There are typically four amounts associated with a single payment: the amount owed by the customer; the amount received by the provider; the amount deducted by the provider; and the final amount paid out by the provider.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Give those fields specific names. A column called amount becomes ambiguous when three systems use that word differently.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For decimal arithmetic, Ruby&#8217;s <a href=\"https:\/\/ruby-doc.org\/stdlib-3.1.1\/libdoc\/bigdecimal\/rdoc\/BigDecimal.html\">BigDecimal documentation<\/a> explains why ordinary binary floating-point types can introduce unexpected differences.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Below is a simple example illustrating a same-currency settlement calculation:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>require \"bigdecimal\"\n\ngross = BigDecimal(\"120.00\")\nfee = BigDecimal(\"1.80\")\nreceived = BigDecimal(\"116.20\")\n\nexpected_net = gross - fee\ndifference = received - expected_net\n\nputs difference.to_s(\"F\") # Expected: -2.0<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In this example, the expected net is 118.20 and the reported receipt is short by 2.00. The example uses identical currency for all three inputs, and it is assumed that the only deduction was for the fee. This is an illustrative arithmetic example, not a full-featured reconciliation service.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reject invalid or non-finite amounts and validate currency codes before comparing values. Use database decimal columns with deliberate precision and scale, or integer units with explicit currency metadata.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Do not silently round every asset to two decimal places.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Make currency conversion visible<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Suppose the order is priced in pounds but the provider reports settlement in euros. Subtracting one from the other would produce a number with no useful meaning.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Store the conversion rate, its direction, the converted amount, and any separate conversion fee supplied by the provider. If required details are missing, flag the entry for review instead of inventing a rate.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The same applies to digital assets. The <a href=\"https:\/\/www.bankofengland.co.uk\/explainers\/what-are-stablecoins-and-how-do-they-work\">Bank of England&#8217;s stablecoin explainer<\/a> describes assets designed to track another value, commonly a currency. Record the actual asset received and the actual settlement currency.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Token balances and bank deposits belong in different fields. Retain blockchain network and provider asset identifier as necessary.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Process duplicate reports safely<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">At some time you&#8217;ll get yesterday&#8217;s report uploaded again. Eventually, a scheduled importer will retry after a timeout.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Choose a stable identity for each imported entry. Prefer the provider&#8217;s documented settlement-line identifier, scoped to the correct merchant account. Enforce uniqueness with a database unique index.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The Rails migration guide explains how to create indexes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Application level validations alone are not sufficient when two workers can simultaneously attempt to import the same row.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reports may contain updates to existing entries. Decide whether a repeated identifier represents an unchanged duplicate or a revision that needs recording.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Provide an import summary that tells the user\/operator how many rows were added, recognized, modified or rejected. &#8220;Import complete&#8221; doesn&#8217;t help much if half of your file had validation errors.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Treat payment notifications as evidence<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If you add webhooks, verify each notification using the provider&#8217;s documented authentication process before trusting its contents.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Persist the verified event before acknowledging successful receipt. Have a background worker pick up unprocessed events, including those left behind after a crash.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Do not mark an order paid because the customer reached a success page. Match the authoritative provider result to the expected merchant account, payment reference, amount, and currency, and check that its documented status permits the transition.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Duplicate messages should not repeat business actions. Use documented event identifiers for deduplication and make state changes conditional on what has already happened.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Rails supports <a href=\"https:\/\/api.rubyonrails.org\/classes\/ActiveRecord\/Locking\/Pessimistic.html\">row-level locking<\/a> for concurrent updates. Use it deliberately around the local records being changed. A database transaction cannot undo an external payment request.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Create a work queue staff can act on<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A useful dashboard can be a fairly plain table.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Display unmatched entries, unexplained differences, missing settlement information, and records waiting too long for updates. Let staff open a row to see the order, payment history, and source report together.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Add a reviewer, a note, and a resolution reason. Staff should be able to record &#8216;provider fee confirmed&#8217; without changing the original imported amount.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Keep reported facts separate from review decisions. Otherwise, a later import could overwrite an explanation or make an adjusted number appear to have come from the provider.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Distinguish provider settlement from receipt in the bank account. Matching a provider report does not, by itself, prove that the bank deposit arrived.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Finish with the cases that break a match<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Prioritize developing duplicate report handling, partial payment handling, refund handling, currency mismatches, and simultaneous worker processing of identical messages.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Give each case an expected outcome: a confirmed match, an explicit mismatch, or a record awaiting review. Include a payment with the right amount but a reference belonging to a different order. Matching numbers alone should never be enough.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Once those cases are understandable on screen, the project has done something useful. The shop owner can follow a payment from order to settlement and see where the explanation is missing.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The order says paid, and the payment provider shows a completed transaction. Yet the bank deposit is smaller than the invoice. Someone has to explain the difference. This gives you a starting point for a Ruby on Rails project: a dashboard that brings together orders, payment records, fees, and settlement reports. Its first version can [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":1654,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[19],"tags":[],"class_list":["post-1652","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ruby-projects"],"blocksy_meta":{"styles_descriptor":{"styles":{"desktop":"","tablet":"","mobile":""},"google_fonts":[],"version":7}},"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Build a Payment Reconciliation Dashboard with Ruby on Rails - Ruby-Doc Learn<\/title>\n<meta name=\"robots\" content=\"noindex, nofollow\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Build a Payment Reconciliation Dashboard with Ruby on Rails - Ruby-Doc Learn\" \/>\n<meta property=\"og:description\" content=\"The order says paid, and the payment provider shows a completed transaction. Yet the bank deposit is smaller than the invoice. Someone has to explain the difference. This gives you a starting point for a Ruby on Rails project: a dashboard that brings together orders, payment records, fees, and settlement reports. Its first version can [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/ruby-doc.org\/learn\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\/\" \/>\n<meta property=\"og:site_name\" content=\"Ruby-Doc Learn\" \/>\n<meta property=\"article:published_time\" content=\"2026-09-23T14:45:42+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/ruby-doc.org\/learn\/wp-content\/uploads\/2026\/09\/17bfadda-cffa-491c-a87a-426cbbce4c4e.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1672\" \/>\n\t<meta property=\"og:image:height\" content=\"941\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"James Britt\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"James Britt\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"6 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\\\/\"},\"author\":{\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/james-britt\\\/#james-britt\"},\"headline\":\"Build a Payment Reconciliation Dashboard with Ruby on Rails\",\"datePublished\":\"2026-09-23T14:45:42+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\\\/\"},\"wordCount\":1236,\"publisher\":{\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/17bfadda-cffa-491c-a87a-426cbbce4c4e.png\",\"articleSection\":[\"Ruby Projects\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\\\/\",\"url\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\\\/\",\"name\":\"Build a Payment Reconciliation Dashboard with Ruby on Rails - Ruby-Doc Learn\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/17bfadda-cffa-491c-a87a-426cbbce4c4e.png\",\"datePublished\":\"2026-09-23T14:45:42+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\\\/#primaryimage\",\"url\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/17bfadda-cffa-491c-a87a-426cbbce4c4e.png\",\"contentUrl\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/17bfadda-cffa-491c-a87a-426cbbce4c4e.png\",\"width\":1672,\"height\":941},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Build a Payment Reconciliation Dashboard with Ruby on Rails\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/#website\",\"url\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/\",\"name\":\"Ruby-Doc Learn\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/#organization\",\"name\":\"Ruby-Doc Learn\",\"url\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/ruby-doc-logo-transparent.png\",\"contentUrl\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/ruby-doc-logo-transparent.png\",\"width\":1650,\"height\":305,\"caption\":\"Ruby-Doc Learn\"},\"image\":{\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/james-britt\\\/#james-britt\",\"name\":\"James Britt\",\"image\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/jgb_self-portrait-20140914.png\",\"description\":\"Creator and long-term maintainer of Ruby-Doc.org, founder of Neurogami, and author of practical Ruby tutorials for Ruby-Doc Learn.\",\"sameAs\":[\"https:\\\/\\\/jamesbritt.com\\\/\",\"https:\\\/\\\/neurogami.com\\\/\",\"https:\\\/\\\/www.oreilly.com\\\/pub\\\/au\\\/2595\",\"https:\\\/\\\/www.rubyevents.org\\\/profiles\\\/james-britt\"],\"url\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/james-britt\\\/\",\"jobTitle\":\"Ruby developer and writer\",\"knowsAbout\":[\"Ruby programming language\",\"Ruby documentation\",\"JRuby\",\"Open Sound Control\",\"Software development\"],\"affiliation\":{\"@type\":\"Organization\",\"name\":\"Ruby-Doc.org\",\"url\":\"https:\\\/\\\/ruby-doc.org\\\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Build a Payment Reconciliation Dashboard with Ruby on Rails - Ruby-Doc Learn","robots":{"index":"noindex","follow":"nofollow"},"og_locale":"en_US","og_type":"article","og_title":"Build a Payment Reconciliation Dashboard with Ruby on Rails - Ruby-Doc Learn","og_description":"The order says paid, and the payment provider shows a completed transaction. Yet the bank deposit is smaller than the invoice. Someone has to explain the difference. This gives you a starting point for a Ruby on Rails project: a dashboard that brings together orders, payment records, fees, and settlement reports. Its first version can [&hellip;]","og_url":"https:\/\/ruby-doc.org\/learn\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\/","og_site_name":"Ruby-Doc Learn","article_published_time":"2026-09-23T14:45:42+00:00","og_image":[{"width":1672,"height":941,"url":"https:\/\/ruby-doc.org\/learn\/wp-content\/uploads\/2026\/09\/17bfadda-cffa-491c-a87a-426cbbce4c4e.png","type":"image\/png"}],"author":"James Britt","twitter_card":"summary_large_image","twitter_misc":{"Written by":"James Britt","Est. reading time":"6 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/ruby-doc.org\/learn\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\/#article","isPartOf":{"@id":"https:\/\/ruby-doc.org\/learn\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\/"},"author":{"@id":"https:\/\/ruby-doc.org\/learn\/james-britt\/#james-britt"},"headline":"Build a Payment Reconciliation Dashboard with Ruby on Rails","datePublished":"2026-09-23T14:45:42+00:00","mainEntityOfPage":{"@id":"https:\/\/ruby-doc.org\/learn\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\/"},"wordCount":1236,"publisher":{"@id":"https:\/\/ruby-doc.org\/learn\/#organization"},"image":{"@id":"https:\/\/ruby-doc.org\/learn\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\/#primaryimage"},"thumbnailUrl":"https:\/\/ruby-doc.org\/learn\/wp-content\/uploads\/2026\/09\/17bfadda-cffa-491c-a87a-426cbbce4c4e.png","articleSection":["Ruby Projects"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/ruby-doc.org\/learn\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\/","url":"https:\/\/ruby-doc.org\/learn\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\/","name":"Build a Payment Reconciliation Dashboard with Ruby on Rails - Ruby-Doc Learn","isPartOf":{"@id":"https:\/\/ruby-doc.org\/learn\/#website"},"primaryImageOfPage":{"@id":"https:\/\/ruby-doc.org\/learn\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\/#primaryimage"},"image":{"@id":"https:\/\/ruby-doc.org\/learn\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\/#primaryimage"},"thumbnailUrl":"https:\/\/ruby-doc.org\/learn\/wp-content\/uploads\/2026\/09\/17bfadda-cffa-491c-a87a-426cbbce4c4e.png","datePublished":"2026-09-23T14:45:42+00:00","breadcrumb":{"@id":"https:\/\/ruby-doc.org\/learn\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/ruby-doc.org\/learn\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ruby-doc.org\/learn\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\/#primaryimage","url":"https:\/\/ruby-doc.org\/learn\/wp-content\/uploads\/2026\/09\/17bfadda-cffa-491c-a87a-426cbbce4c4e.png","contentUrl":"https:\/\/ruby-doc.org\/learn\/wp-content\/uploads\/2026\/09\/17bfadda-cffa-491c-a87a-426cbbce4c4e.png","width":1672,"height":941},{"@type":"BreadcrumbList","@id":"https:\/\/ruby-doc.org\/learn\/build-a-payment-reconciliation-dashboard-with-ruby-on-rails\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/ruby-doc.org\/learn\/"},{"@type":"ListItem","position":2,"name":"Build a Payment Reconciliation Dashboard with Ruby on Rails"}]},{"@type":"WebSite","@id":"https:\/\/ruby-doc.org\/learn\/#website","url":"https:\/\/ruby-doc.org\/learn\/","name":"Ruby-Doc Learn","description":"","publisher":{"@id":"https:\/\/ruby-doc.org\/learn\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/ruby-doc.org\/learn\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/ruby-doc.org\/learn\/#organization","name":"Ruby-Doc Learn","url":"https:\/\/ruby-doc.org\/learn\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ruby-doc.org\/learn\/#\/schema\/logo\/image\/","url":"https:\/\/ruby-doc.org\/learn\/wp-content\/uploads\/2026\/09\/ruby-doc-logo-transparent.png","contentUrl":"https:\/\/ruby-doc.org\/learn\/wp-content\/uploads\/2026\/09\/ruby-doc-logo-transparent.png","width":1650,"height":305,"caption":"Ruby-Doc Learn"},"image":{"@id":"https:\/\/ruby-doc.org\/learn\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/ruby-doc.org\/learn\/james-britt\/#james-britt","name":"James Britt","image":"https:\/\/ruby-doc.org\/learn\/wp-content\/uploads\/2026\/09\/jgb_self-portrait-20140914.png","description":"Creator and long-term maintainer of Ruby-Doc.org, founder of Neurogami, and author of practical Ruby tutorials for Ruby-Doc Learn.","sameAs":["https:\/\/jamesbritt.com\/","https:\/\/neurogami.com\/","https:\/\/www.oreilly.com\/pub\/au\/2595","https:\/\/www.rubyevents.org\/profiles\/james-britt"],"url":"https:\/\/ruby-doc.org\/learn\/james-britt\/","jobTitle":"Ruby developer and writer","knowsAbout":["Ruby programming language","Ruby documentation","JRuby","Open Sound Control","Software development"],"affiliation":{"@type":"Organization","name":"Ruby-Doc.org","url":"https:\/\/ruby-doc.org\/"}}]}},"_links":{"self":[{"href":"https:\/\/ruby-doc.org\/learn\/wp-json\/wp\/v2\/posts\/1652","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ruby-doc.org\/learn\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ruby-doc.org\/learn\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ruby-doc.org\/learn\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/ruby-doc.org\/learn\/wp-json\/wp\/v2\/comments?post=1652"}],"version-history":[{"count":1,"href":"https:\/\/ruby-doc.org\/learn\/wp-json\/wp\/v2\/posts\/1652\/revisions"}],"predecessor-version":[{"id":1653,"href":"https:\/\/ruby-doc.org\/learn\/wp-json\/wp\/v2\/posts\/1652\/revisions\/1653"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ruby-doc.org\/learn\/wp-json\/wp\/v2\/media\/1654"}],"wp:attachment":[{"href":"https:\/\/ruby-doc.org\/learn\/wp-json\/wp\/v2\/media?parent=1652"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ruby-doc.org\/learn\/wp-json\/wp\/v2\/categories?post=1652"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ruby-doc.org\/learn\/wp-json\/wp\/v2\/tags?post=1652"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}