Refactoring Google Payment

Refactoring Google Payment

Simplifying Google Payment Tests with FakeGooglePurchaseServer: Power of Abstraction

Testing in-app purchases involving Google Payments can be a nightmare. Current methods require extensive setup, including stubbing requests for various response scenarios from the Google Payment server. This can make tests difficult to read and maintain

let!(:google_sku) { '' }
let!(:partner_product) { create :partner_product, product_catalog: non_recurring_product_catalog, partner: :google, code: google_sku }
let!(:invalid_state_json) {
    "error" => {
      "errors" => [
          "domain" => "androidpublisher",
          "reason" => "invalidPurchaseState",
          "message" => "The purchase is not in a valid state to perform the desired operation.",
          "location" => "token",
          "locationType" => "parameter"
      "code" => 400,
      "message" => "The purchase is not in a valid state to perform the desired operation."

expected_ack_url = "#{config.publisher_url}/#{config.package_name}/purchases/products/#{google_sku}/tokens/#{purchase_token}:acknowledge"
stub_request(:post, expected_ack_url)
  .to_return(status: 400, body: invalid_state_json, headers: { 'Content-Type' => 'application/json; charset=utf-8' })

pending_google_receipt = {
  'packageName' => config.package_name,
  'acknowledged' => false,
  'orderId' => order_id,
  'productId' => google_sku,
  'developerPayload' => '',
  'purchaseTime' => 0,
  'purchaseState' => 2,
  'purchaseToken' => purchase_token

pending_payload = {
  purchases: [
      original_json: pending_google_receipt,
      signature: sign(pending_google_receipt)

This initiative is to propose a solution: the FakeGooglePurchaseServer class. But what truly makes this approach powerful is the concept of abstraction.

The Problem of Complexity

Currently, testing Google Payments involves:

  • Manually stubbing requests for different Google responses (success, failure, etc.)
  • Repeating this setup across multiple tests
  • This leads to cryptic and bloated test code, filled with intricate details about Google's specific responses.

The Power of Abstraction: Enter FakeGooglePurchaseServer

FakeGooglePurchaseServer simplifies Google Payment testing by abstracting away the complexities of the Google payment server. It acts as a mock server within your tests, enabling you to interact with a simulated Google server without needing to know the specifics of every Google response code or message.

Benefits of Abstraction

  • Improved test readability: Developers can easily understand the intent of a test without deciphering complex stubs related to Google's inner workings. The focus is on the expected behavior of your code, not the intricacies of Google's API.
  • Reduced code duplication: No need to repeatedly set up stubs for different scenarios. FakeGooglePurchaseServer acts as a single point of configuration for all your Google Payment test interactions.
  • Easier maintenance: Changes to Google-related logic are isolated within FakeGooglePurchaseServer. If Google's response format changes, you only need to update FakeGooglePurchaseServer, not all your tests.

Example with Abstraction

let(:google_server) {'google-access-token') }
before { google_server.product_ack(purchase).returns_invalid_state }

This example focuses on the core functionality you want to test: how your code behaves when receiving an "invalid state" response for product acknowledgment. FakeGooglePurchaseServer handles the abstraction of generating that specific Google-like response. Your test becomes clear and concise.


FakeGooglePurchaseServer leverages the power of abstraction to help developers write cleaner, more maintainable tests for Google Payment functionality. Its clear interface allows developers to focus on testing specific behaviors without getting bogged down in the complexities of setting up and understanding every detail of Google's payment responses. This abstraction makes your tests more readable, maintainable, and future-proof.