Full Code of nov/paypal-express for AI

master 198de9274df8 cached
85 files
119.0 KB
37.4k tokens
144 symbols
1 requests
Download .txt
Repository: nov/paypal-express
Branch: master
Commit: 198de9274df8
Files: 85
Total size: 119.0 KB

Directory structure:
gitextract_wg6sknv5/

├── .document
├── .github/
│   └── FUNDING.yml
├── .gitignore
├── .rspec
├── .travis.yml
├── Gemfile
├── LICENSE
├── README.rdoc
├── Rakefile
├── VERSION
├── lib/
│   ├── paypal/
│   │   ├── base.rb
│   │   ├── exception/
│   │   │   ├── api_error.rb
│   │   │   └── http_error.rb
│   │   ├── exception.rb
│   │   ├── express/
│   │   │   ├── request.rb
│   │   │   └── response.rb
│   │   ├── express.rb
│   │   ├── ipn.rb
│   │   ├── nvp/
│   │   │   ├── request.rb
│   │   │   └── response.rb
│   │   ├── payment/
│   │   │   ├── common/
│   │   │   │   └── amount.rb
│   │   │   ├── recurring/
│   │   │   │   ├── activation.rb
│   │   │   │   ├── billing.rb
│   │   │   │   └── summary.rb
│   │   │   ├── recurring.rb
│   │   │   ├── request/
│   │   │   │   └── item.rb
│   │   │   ├── request.rb
│   │   │   ├── response/
│   │   │   │   ├── address.rb
│   │   │   │   ├── info.rb
│   │   │   │   ├── item.rb
│   │   │   │   ├── payer.rb
│   │   │   │   ├── reference.rb
│   │   │   │   └── refund.rb
│   │   │   └── response.rb
│   │   └── util.rb
│   └── paypal.rb
├── paypal-express.gemspec
└── spec/
    ├── fake_response/
    │   ├── BillAgreementUpdate/
    │   │   ├── fetch.txt
    │   │   └── revoke.txt
    │   ├── CreateBillingAgreement/
    │   │   └── success.txt
    │   ├── CreateRecurringPaymentsProfile/
    │   │   ├── failure.txt
    │   │   └── success.txt
    │   ├── DoCapture/
    │   │   ├── failure.txt
    │   │   └── success.txt
    │   ├── DoExpressCheckoutPayment/
    │   │   ├── failure.txt
    │   │   ├── success.txt
    │   │   ├── success_with_billing_agreement.txt
    │   │   └── success_with_many_items.txt
    │   ├── DoReferenceTransaction/
    │   │   ├── failure.txt
    │   │   └── success.txt
    │   ├── DoVoid/
    │   │   └── success.txt
    │   ├── GetExpressCheckoutDetails/
    │   │   ├── failure.txt
    │   │   ├── success.txt
    │   │   └── with_billing_accepted_status.txt
    │   ├── GetRecurringPaymentsProfileDetails/
    │   │   ├── failure.txt
    │   │   └── success.txt
    │   ├── GetTransactionDetails/
    │   │   ├── failure.txt
    │   │   └── success.txt
    │   ├── IPN/
    │   │   ├── invalid.txt
    │   │   └── valid.txt
    │   ├── ManageRecurringPaymentsProfileStatus/
    │   │   ├── failure.txt
    │   │   └── success.txt
    │   ├── RefundTransaction/
    │   │   └── full.txt
    │   └── SetExpressCheckout/
    │       ├── failure.txt
    │       └── success.txt
    ├── helpers/
    │   └── fake_response_helper.rb
    ├── paypal/
    │   ├── exception/
    │   │   ├── api_error_spec.rb
    │   │   └── http_error_spec.rb
    │   ├── express/
    │   │   ├── request_spec.rb
    │   │   └── response_spec.rb
    │   ├── ipn_spec.rb
    │   ├── nvp/
    │   │   ├── request_spec.rb
    │   │   └── response_spec.rb
    │   ├── payment/
    │   │   ├── common/
    │   │   │   └── amount_spec.rb
    │   │   ├── recurring/
    │   │   │   └── activation_spec.rb
    │   │   ├── recurring_spec.rb
    │   │   ├── request/
    │   │   │   └── item_spec.rb
    │   │   ├── request_spec.rb
    │   │   ├── response/
    │   │   │   ├── address_spec.rb
    │   │   │   ├── info_spec.rb
    │   │   │   ├── item_spec.rb
    │   │   │   └── payer_spec.rb
    │   │   └── response_spec.rb
    │   └── util_spec.rb
    └── spec_helper.rb

================================================
FILE CONTENTS
================================================

================================================
FILE: .document
================================================
README.rdoc
lib/**/*.rb
bin/*
features/**/*.feature
LICENSE


================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

github: nov


================================================
FILE: .gitignore
================================================
## MAC OS
.DS_Store

## TEXTMATE
*.tmproj
tmtags

## EMACS
*~
\#*
.\#*

## VIM
*.swp

## PROJECT::GENERAL
coverage*
rdoc
pkg

## PROJECT::SPECIFIC
Gemfile.lock


================================================
FILE: .rspec
================================================
--color
--format=documentation


================================================
FILE: .travis.yml
================================================
before_install:
  - gem install bundler

rvm:
  - 2.0.0
  - 2.1.2

================================================
FILE: Gemfile
================================================
source 'http://rubygems.org'

platform :jruby do
  gem 'jruby-openssl', '>= 0.7'
end

gemspec

================================================
FILE: LICENSE
================================================
Copyright (c) 2011 nov matake

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: README.rdoc
================================================
= paypal-express

Handle PayPal Express Checkout.
Both Instance Payment and Recurring Payment are supported.
Express Checkout for Digital Goods is also supported.

{<img src="https://secure.travis-ci.org/nov/paypal-express.png" />}[http://travis-ci.org/nov/paypal-express]

== Installation

  gem install paypal-express

== Usage

See Wiki on Github
https://github.com/nov/paypal-express/wiki

Play with Sample Rails App
https://github.com/nov/paypal-express-sample
https://paypal-express-sample.heroku.com

== Note on Patches/Pull Requests

* Fork the project.
* Make your feature addition or bug fix.
* Add tests for it. This is important so I don't break it in a
  future version unintentionally.
* Commit, do not mess with rakefile, version, or history.
  (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
* Send me a pull request. Bonus points for topic branches.

== Copyright

Copyright (c) 2011 nov matake. See LICENSE for details.

================================================
FILE: Rakefile
================================================
require 'bundler'
Bundler::GemHelper.install_tasks

require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec)

namespace :coverage do
  desc "Open coverage report"
  task :report do
    require 'simplecov'
    `open "#{File.join SimpleCov.coverage_path, 'index.html'}"`
  end
end

task :spec do
  Rake::Task[:'coverage:report'].invoke unless ENV['TRAVIS_RUBY_VERSION']
end

task :default => :spec

================================================
FILE: VERSION
================================================
0.8.1

================================================
FILE: lib/paypal/base.rb
================================================
module Paypal
  class Base
    include AttrRequired, AttrOptional, Util

    def initialize(attributes = {})
      if attributes.is_a?(Hash)
        (required_attributes + optional_attributes).each do |key|
          value = if numeric_attribute?(key)
            Util.to_numeric(attributes[key])
          else
            attributes[key]
          end
          self.send "#{key}=", value
        end
      end
      attr_missing!
    end
  end
end

================================================
FILE: lib/paypal/exception/api_error.rb
================================================
module Paypal
  class Exception
    class APIError < Exception
      attr_accessor :response
      def initialize(response = {})
        @response = if response.is_a?(Hash)
          Response.new response
        else
          response
        end
      end

      def message
        if response.respond_to?(:short_messages) && response.short_messages.any?
          "PayPal API Error: " <<
            response.short_messages.map{ |m| "'#{m}'" }.join(", ")
        else
          "PayPal API Error"
        end
      end

      class Response
        cattr_reader :attribute_mapping
        @@attribute_mapping = {
          :ACK => :ack,
          :BUILD => :build,
          :CORRELATIONID => :correlation_id,
          :TIMESTAMP => :timestamp,
          :VERSION => :version,
          :ORDERTIME => :order_time,
          :PENDINGREASON => :pending_reason,
          :PAYMENTSTATUS => :payment_status,
          :PAYMENTTYPE => :payment_type,
          :REASONCODE => :reason_code,
          :TRANSACTIONTYPE => :transaction_type
        }
        attr_accessor *@@attribute_mapping.values
        attr_accessor :raw, :details
        alias_method :colleration_id, :correlation_id # NOTE: I made a typo :p

        class Detail
          cattr_reader :attribute_mapping
          @@attribute_mapping = {
            :ERRORCODE => :error_code,
            :SEVERITYCODE => :severity_code,
            :LONGMESSAGE => :long_message,
            :SHORTMESSAGE => :short_message
          }
          attr_accessor *@@attribute_mapping.values

          def initialize(attributes = {})
            @raw = attributes
            attrs = attributes.dup
            @@attribute_mapping.each do |key, value|
              self.send "#{value}=", attrs.delete(key)
            end

            # warn ignored params
            attrs.each do |key, value|
              Paypal.log "Ignored Parameter (#{self.class}): #{key}=#{value}", :warn
            end
          end
        end

        def initialize(attributes = {})
          attrs = attributes.dup
          @raw = attributes
          @@attribute_mapping.each do |key, value|
            self.send "#{value}=", attrs.delete(key)
          end
          details = []
          attrs.keys.each do |attribute|
            key, index = attribute.to_s.scan(/^L_(\S+)(\d+)$/).first
            next if [key, index].any?(&:blank?)
            details[index.to_i] ||= {}
            details[index.to_i][key.to_sym] = attrs.delete(attribute)
          end
          @details = details.collect do |_attrs_|
            Detail.new _attrs_
          end

          # warn ignored params
          attrs.each do |key, value|
            Paypal.log "Ignored Parameter (#{self.class}): #{key}=#{value}", :warn
          end
        end

        def short_messages
          details.map(&:short_message).compact
        end
      end
    end
  end
end


================================================
FILE: lib/paypal/exception/http_error.rb
================================================
module Paypal
  class Exception
    class HttpError < Exception
      attr_accessor :code, :message, :body
      def initialize(code, message, body = '')
        @code = code
        @message = message
        @body = body
      end
    end
  end
end

================================================
FILE: lib/paypal/exception.rb
================================================
module Paypal
  class Exception < StandardError
  end
end

================================================
FILE: lib/paypal/express/request.rb
================================================
module Paypal
  module Express
    class Request < NVP::Request

      # Common

      def setup(payment_requests, return_url, cancel_url, options = {})
        params = {
          :RETURNURL => return_url,
          :CANCELURL => cancel_url
        }
        if options[:no_shipping]
          params[:REQCONFIRMSHIPPING] = 0
          params[:NOSHIPPING] = 1
        end

        params[:ALLOWNOTE] = 0 if options[:allow_note] == false

        {
          :solution_type => :SOLUTIONTYPE,
          :landing_page  => :LANDINGPAGE,
          :email         => :EMAIL,
          :brand         => :BRANDNAME,
          :locale        => :LOCALECODE,
          :logo          => :LOGOIMG,
          :cart_border_color => :CARTBORDERCOLOR,
          :payflow_color => :PAYFLOWCOLOR
        }.each do |option_key, param_key|
          params[param_key] = options[option_key] if options[option_key]
        end
        Array(payment_requests).each_with_index do |payment_request, index|
          params.merge! payment_request.to_params(index)
        end
        response = self.request :SetExpressCheckout, params
        Response.new response, options
      end

      def details(token)
        response = self.request :GetExpressCheckoutDetails, {:TOKEN => token}
        Response.new response
      end

      def transaction_details(transaction_id)
        response = self.request :GetTransactionDetails, {:TRANSACTIONID=> transaction_id}
        Response.new response
      end

      def checkout!(token, payer_id, payment_requests)
        params = {
          :TOKEN => token,
          :PAYERID => payer_id
        }
        Array(payment_requests).each_with_index do |payment_request, index|
          params.merge! payment_request.to_params(index)
        end
        response = self.request :DoExpressCheckoutPayment, params
        Response.new response
      end

      def capture!(authorization_id, amount, currency_code, complete_type = 'Complete')
        params = {
          :AUTHORIZATIONID => authorization_id,
          :COMPLETETYPE => complete_type,
          :AMT => amount,
          :CURRENCYCODE => currency_code
        }

        response = self.request :DoCapture, params
        Response.new response
      end

      def void!(authorization_id, params={})
        params = {
          :AUTHORIZATIONID => authorization_id,
          :NOTE => params[:note]
        }

        response = self.request :DoVoid, params
        Response.new response
      end

      # Recurring Payment Specific

      def subscribe!(token, recurring_profile)
        params = {
          :TOKEN => token
        }
        params.merge! recurring_profile.to_params
        response = self.request :CreateRecurringPaymentsProfile, params
        Response.new response
      end

      def subscription(profile_id)
        params = {
          :PROFILEID => profile_id
        }
        response = self.request :GetRecurringPaymentsProfileDetails, params
        Response.new response
      end

      def renew!(profile_id, action, options = {})
        params = {
          :PROFILEID => profile_id,
          :ACTION => action
        }
        if options[:note]
          params[:NOTE] = options[:note]
        end
        response = self.request :ManageRecurringPaymentsProfileStatus, params
        Response.new response
      end

      def suspend!(profile_id, options = {})
        renew!(profile_id, :Suspend, options)
      end

      def cancel!(profile_id, options = {})
        renew!(profile_id, :Cancel, options)
      end

      def reactivate!(profile_id, options = {})
        renew!(profile_id, :Reactivate, options)
      end


      # Reference Transaction Specific

      def agree!(token, options = {})
        params = {
          :TOKEN => token
        }
        if options[:max_amount]
          params[:MAXAMT] = Util.formatted_amount options[:max_amount]
        end
        response = self.request :CreateBillingAgreement, params
        Response.new response
      end

      def agreement(reference_id)
        params = {
          :REFERENCEID => reference_id
        }
        response = self.request :BillAgreementUpdate, params
        Response.new response
      end

      def charge!(reference_id, amount, options = {})
        params = {
          :REFERENCEID => reference_id,
          :AMT => Util.formatted_amount(amount),
          :PAYMENTACTION => options[:payment_action] || :Sale
        }
        if options[:currency_code]
          params[:CURRENCYCODE] = options[:currency_code]
        end
        response = self.request :DoReferenceTransaction, params
        Response.new response
      end

      def revoke!(reference_id)
        params = {
          :REFERENCEID => reference_id,
          :BillingAgreementStatus => :Canceled
        }
        response = self.request :BillAgreementUpdate, params
        Response.new response
      end


      # Refund Specific

      def refund!(transaction_id, options = {})
        params = {
          :TRANSACTIONID => transaction_id,
          :REFUNDTYPE => :Full
        }
        if options[:invoice_id]
          params[:INVOICEID] = options[:invoice_id]
        end
        if options[:type]
          params[:REFUNDTYPE] = options[:type]
          params[:AMT] = options[:amount]
          params[:CURRENCYCODE] = options[:currency_code]
        end
        if options[:note]
          params[:NOTE] = options[:note]
        end
        response = self.request :RefundTransaction, params
        Response.new response
      end

    end
  end
end


================================================
FILE: lib/paypal/express/response.rb
================================================
module Paypal
  module Express
    class Response < NVP::Response
      attr_accessor :pay_on_paypal, :mobile

      def initialize(response, options = {})
        super response
        @pay_on_paypal = options[:pay_on_paypal]
        @mobile        = options[:mobile]
      end

      def redirect_uri
        endpoint = URI.parse Paypal.endpoint
        endpoint.query = query(:with_cmd).to_query
        endpoint.to_s
      end

      def popup_uri
        endpoint = URI.parse Paypal.popup_endpoint
        endpoint.query = query.to_query
        endpoint.to_s
      end

      private

      def query(with_cmd = false)
        _query_ = {:token => self.token}
        _query_.merge!(:cmd => '_express-checkout')        if with_cmd
        _query_.merge!(:cmd => '_express-checkout-mobile') if mobile
        _query_.merge!(:useraction => 'commit')            if pay_on_paypal
        _query_
      end
    end
  end
end

================================================
FILE: lib/paypal/express.rb
================================================
require 'paypal'

================================================
FILE: lib/paypal/ipn.rb
================================================
module Paypal
  module IPN
    def self.endpoint
      _endpoint_ = URI.parse Paypal.endpoint
      _endpoint_.query = {
        :cmd => '_notify-validate'
      }.to_query
      _endpoint_.to_s
    end

    def self.verify!(raw_post)
      response = RestClient.post(
        endpoint, raw_post
      )
      case response.body
      when 'VERIFIED'
        true
      else
        raise Exception::APIError.new(response.body)
      end
    end
  end
end

================================================
FILE: lib/paypal/nvp/request.rb
================================================
module Paypal
  module NVP
    class Request < Base
      attr_required :username, :password, :signature
      attr_optional :subject
      attr_accessor :version

      ENDPOINT = {
        :production => 'https://api-3t.paypal.com/nvp',
        :sandbox => 'https://api-3t.sandbox.paypal.com/nvp'
      }

      def self.endpoint
        if Paypal.sandbox?
          ENDPOINT[:sandbox]
        else
          ENDPOINT[:production]
        end
      end

      def initialize(attributes = {})
        @version = Paypal.api_version
        super
      end

      def common_params
        {
          :USER => self.username,
          :PWD => self.password,
          :SIGNATURE => self.signature,
          :SUBJECT => self.subject,
          :VERSION => self.version
        }
      end

      def request(method, params = {})
        handle_response do
          post(method, params)
        end
      end

      private

      def post(method, params)
        RestClient.post(self.class.endpoint, common_params.merge(params).merge(:METHOD => method))
      end

      def handle_response
        response = yield
        response = CGI.parse(response).inject({}) do |res, (k, v)|
          res.merge!(k.to_sym => v.first)
        end
        case response[:ACK]
        when 'Success', 'SuccessWithWarning'
          response
        else
          raise Exception::APIError.new(response)
        end
      rescue RestClient::Exception => e
        raise Exception::HttpError.new(e.http_code, e.message, e.http_body)
      end
    end
  end
end

================================================
FILE: lib/paypal/nvp/response.rb
================================================
module Paypal
  module NVP
    class Response < Base
      cattr_reader :attribute_mapping
      @@attribute_mapping = {
        :ACK => :ack,
        :BUILD => :build,
        :BILLINGAGREEMENTACCEPTEDSTATUS => :billing_agreement_accepted_status,
        :CHECKOUTSTATUS => :checkout_status,
        :CORRELATIONID => :correlation_id,
        :COUNTRYCODE => :country_code,
        :CURRENCYCODE => :currency_code,
        :DESC => :description,
        :NOTIFYURL => :notify_url,
        :TIMESTAMP => :timestamp,
        :TOKEN => :token,
        :VERSION => :version,
        # Some of the attributes below are duplicates of what
        # exists in the payment response, but paypal doesn't
        # prefix these with PAYMENTREQUEST when issuing a
        # GetTransactionDetails response.
        :RECEIVEREMAIL => :receiver_email,
        :RECEIVERID => :receiver_id,
        :SUBJECT => :subject,
        :TRANSACTIONID => :transaction_id,
        :TRANSACTIONTYPE => :transaction_type,
        :PAYMENTTYPE => :payment_type,
        :ORDERTIME => :order_time,
        :PAYMENTSTATUS => :payment_status,
        :PENDINGREASON => :pending_reason,
        :REASONCODE => :reason_code,
        :PROTECTIONELIGIBILITY => :protection_eligibility,
        :PROTECTIONELIGIBILITYTYPE => :protection_eligibility_type,
        :ADDRESSOWNER => :address_owner,
        :ADDRESSSTATUS => :address_status,
        :INVNUM => :invoice_number,
        :CUSTOM => :custom
      }
      attr_accessor *@@attribute_mapping.values
      attr_accessor :shipping_options_is_default, :success_page_redirect_requested, :insurance_option_selected
      attr_accessor :amount, :description, :ship_to, :bill_to, :payer, :recurring, :billing_agreement, :refund
      attr_accessor :payment_responses, :payment_info, :items
      alias_method :colleration_id, :correlation_id # NOTE: I made a typo :p

      def initialize(attributes = {})
        attrs = attributes.dup
        @@attribute_mapping.each do |key, value|
          self.send "#{value}=", attrs.delete(key)
        end
        @shipping_options_is_default = attrs.delete(:SHIPPINGOPTIONISDEFAULT) == 'true'
        @success_page_redirect_requested = attrs.delete(:SUCCESSPAGEREDIRECTREQUESTED) == 'true'
        @insurance_option_selected = attrs.delete(:INSURANCEOPTIONSELECTED) == 'true'
        @amount = Payment::Common::Amount.new(
          :total => attrs.delete(:AMT),
          :item => attrs.delete(:ITEMAMT),
          :handing => attrs.delete(:HANDLINGAMT),
          :insurance => attrs.delete(:INSURANCEAMT),
          :ship_disc => attrs.delete(:SHIPDISCAMT),
          :shipping => attrs.delete(:SHIPPINGAMT),
          :tax => attrs.delete(:TAXAMT),
          :fee => attrs.delete(:FEEAMT)
        )
        @ship_to = Payment::Response::Address.new(
          :owner => attrs.delete(:SHIPADDRESSOWNER),
          :status => attrs.delete(:SHIPADDRESSSTATUS),
          :name => attrs.delete(:SHIPTONAME),
          :zip => attrs.delete(:SHIPTOZIP),
          :street => attrs.delete(:SHIPTOSTREET),
          :street2 => attrs.delete(:SHIPTOSTREET2),
          :city => attrs.delete(:SHIPTOCITY),
          :state => attrs.delete(:SHIPTOSTATE),
          :country_code => attrs.delete(:SHIPTOCOUNTRYCODE),
          :country_name => attrs.delete(:SHIPTOCOUNTRYNAME)
        )
        @bill_to = Payment::Response::Address.new(
          :owner => attrs.delete(:ADDRESSID),
          :status => attrs.delete(:ADDRESSSTATUS),
          :name => attrs.delete(:BILLINGNAME),
          :zip => attrs.delete(:ZIP),
          :street => attrs.delete(:STREET),
          :street2 => attrs.delete(:STREET2),
          :city => attrs.delete(:CITY),
          :state => attrs.delete(:STATE),
          :country_code => attrs.delete(:COUNTRY)
        )
        if attrs[:PAYERID]
          @payer = Payment::Response::Payer.new(
            :identifier => attrs.delete(:PAYERID),
            :status => attrs.delete(:PAYERSTATUS),
            :first_name => attrs.delete(:FIRSTNAME),
            :last_name => attrs.delete(:LASTNAME),
            :email => attrs.delete(:EMAIL),
            :company => attrs.delete(:BUSINESS)
          )
        end
        if attrs[:PROFILEID]
          @recurring = Payment::Recurring.new(
            :identifier => attrs.delete(:PROFILEID),
            # NOTE:
            #  CreateRecurringPaymentsProfile returns PROFILESTATUS
            #  GetRecurringPaymentsProfileDetails returns STATUS
            :description => @description,
            :status => attrs.delete(:STATUS) || attrs.delete(:PROFILESTATUS),
            :start_date => attrs.delete(:PROFILESTARTDATE),
            :name => attrs.delete(:SUBSCRIBERNAME),
            :reference => attrs.delete(:PROFILEREFERENCE),
            :auto_bill => attrs.delete(:AUTOBILLOUTAMT),
            :max_fails => attrs.delete(:MAXFAILEDPAYMENTS),
            :aggregate_amount => attrs.delete(:AGGREGATEAMT),
            :aggregate_optional_amount => attrs.delete(:AGGREGATEOPTIONALAMT),
            :final_payment_date => attrs.delete(:FINALPAYMENTDUEDATE)
          )
          if attrs[:BILLINGPERIOD]
            @recurring.billing = Payment::Recurring::Billing.new(
              :amount => @amount,
              :currency_code => @currency_code,
              :period => attrs.delete(:BILLINGPERIOD),
              :frequency => attrs.delete(:BILLINGFREQUENCY),
              :total_cycles => attrs.delete(:TOTALBILLINGCYCLES),
              :trial => {
                :period => attrs.delete(:TRIALBILLINGPERIOD),
                :frequency => attrs.delete(:TRIALBILLINGFREQUENCY),
                :total_cycles => attrs.delete(:TRIALTOTALBILLINGCYCLES),
                :currency_code => attrs.delete(:TRIALCURRENCYCODE),
                :amount => attrs.delete(:TRIALAMT),
                :tax_amount => attrs.delete(:TRIALTAXAMT),
                :shipping_amount => attrs.delete(:TRIALSHIPPINGAMT),
                :paid => attrs.delete(:TRIALAMTPAID)
              }
            )
          end
          if attrs[:REGULARAMT]
            @recurring.regular_billing = Payment::Recurring::Billing.new(
              :amount => attrs.delete(:REGULARAMT),
              :shipping_amount => attrs.delete(:REGULARSHIPPINGAMT),
              :tax_amount => attrs.delete(:REGULARTAXAMT),
              :currency_code => attrs.delete(:REGULARCURRENCYCODE),
              :period => attrs.delete(:REGULARBILLINGPERIOD),
              :frequency => attrs.delete(:REGULARBILLINGFREQUENCY),
              :total_cycles => attrs.delete(:REGULARTOTALBILLINGCYCLES),
              :paid => attrs.delete(:REGULARAMTPAID)
            )
            @recurring.summary = Payment::Recurring::Summary.new(
              :next_billing_date => attrs.delete(:NEXTBILLINGDATE),
              :cycles_completed => attrs.delete(:NUMCYCLESCOMPLETED),
              :cycles_remaining => attrs.delete(:NUMCYCLESREMAINING),
              :outstanding_balance => attrs.delete(:OUTSTANDINGBALANCE),
              :failed_count => attrs.delete(:FAILEDPAYMENTCOUNT),
              :last_payment_date => attrs.delete(:LASTPAYMENTDATE),
              :last_payment_amount => attrs.delete(:LASTPAYMENTAMT)
            )
          end
        end
        if attrs[:BILLINGAGREEMENTID]
          @billing_agreement = Payment::Response::Reference.new(
            :identifier => attrs.delete(:BILLINGAGREEMENTID),
            :description => attrs.delete(:BILLINGAGREEMENTDESCRIPTION),
            :status => attrs.delete(:BILLINGAGREEMENTSTATUS)
          )
          billing_agreement_info = Payment::Response::Info.attribute_mapping.keys.inject({}) do |billing_agreement_info, key|
            billing_agreement_info.merge! key => attrs.delete(key)
          end
          @billing_agreement.info = Payment::Response::Info.new billing_agreement_info
          @billing_agreement.info.amount = @amount
        end
        if attrs[:REFUNDTRANSACTIONID]
          @refund = Payment::Response::Refund.new(
            :transaction_id => attrs.delete(:REFUNDTRANSACTIONID),
            :amount => {
              :total => attrs.delete(:TOTALREFUNDEDAMOUNT),
              :fee => attrs.delete(:FEEREFUNDAMT),
              :gross => attrs.delete(:GROSSREFUNDAMT),
              :net => attrs.delete(:NETREFUNDAMT)
            }
          )
        end

        # payment_responses
        payment_responses = []
        attrs.keys.each do |_attr_|
          prefix, index, key = case _attr_.to_s
          when /^PAYMENTREQUEST/, /^PAYMENTREQUESTINFO/
            _attr_.to_s.split('_')
          when /^L_PAYMENTREQUEST/
            _attr_.to_s.split('_')[1..-1]
          end
          if prefix
            payment_responses[index.to_i] ||= {}
            payment_responses[index.to_i][key.to_sym] = attrs.delete(_attr_)
          end
        end
        @payment_responses = payment_responses.collect do |_attrs_|
          Payment::Response.new _attrs_
        end

        # payment_info
        payment_info = []
        attrs.keys.each do |_attr_|
          prefix, index, key = _attr_.to_s.split('_')
          if prefix == 'PAYMENTINFO'
            payment_info[index.to_i] ||= {}
            payment_info[index.to_i][key.to_sym] = attrs.delete(_attr_)
          end
        end
        @payment_info = payment_info.collect do |_attrs_|
          Payment::Response::Info.new _attrs_
        end

        # payment_info
        items = []
        attrs.keys.each do |_attr_|
          key, index = _attr_.to_s.scan(/^L_(.+?)(\d+)$/).flatten
          if index
            items[index.to_i] ||= {}
            items[index.to_i][key.to_sym] = attrs.delete(_attr_)
          end
        end
        @items = items.collect do |_attrs_|
          Payment::Response::Item.new _attrs_
        end

        # remove duplicated parameters
        attrs.delete(:SHIPTOCOUNTRY) # NOTE: Same with SHIPTOCOUNTRYCODE
        attrs.delete(:SALESTAX) # Same as TAXAMT

        # warn ignored attrs
        attrs.each do |key, value|
          Paypal.log "Ignored Parameter (#{self.class}): #{key}=#{value}", :warn
        end
      end
    end
  end
end


================================================
FILE: lib/paypal/payment/common/amount.rb
================================================
module Paypal
  module Payment
    module Common
      class Amount < Base
        attr_optional :total, :item, :fee, :handing, :insurance, :ship_disc, :shipping, :tax, :gross, :net

        def numeric_attribute?(key)
          true
        end
      end
    end
  end
end


================================================
FILE: lib/paypal/payment/recurring/activation.rb
================================================
module Paypal
  module Payment
    class Recurring::Activation < Base
      attr_optional :initial_amount, :failed_action

      def to_params
        {
          :INITAMT => Util.formatted_amount(self.initial_amount),
          :FAILEDINITAMTACTION => self.failed_action
        }
      end
    end
  end
end

================================================
FILE: lib/paypal/payment/recurring/billing.rb
================================================
module Paypal
  module Payment
    class Recurring::Billing < Base
      attr_optional :period, :frequency, :paid, :currency_code, :total_cycles
      attr_accessor :amount, :trial

      def initialize(attributes = {})
        @amount = if attributes[:amount].is_a?(Common::Amount)
          attributes[:amount]
        else
          Common::Amount.new(
            :total => attributes[:amount],
            :tax => attributes[:tax_amount],
            :shipping => attributes[:shipping_amount]
          )
        end
        @trial = Recurring::Billing.new(attributes[:trial]) if attributes[:trial].present?
        super
      end

      def to_params
        trial_params = (trial.try(:to_params) || {}).inject({}) do |trial_params, (key, value)|
          trial_params.merge(
            :"TRIAL#{key}" => value
          )
        end
        trial_params.merge(
          :BILLINGPERIOD => self.period,
          :BILLINGFREQUENCY => self.frequency,
          :TOTALBILLINGCYCLES => self.total_cycles,
          :AMT => Util.formatted_amount(self.amount.total),
          :CURRENCYCODE => self.currency_code,
          :SHIPPINGAMT => Util.formatted_amount(self.amount.shipping),
          :TAXAMT => Util.formatted_amount(self.amount.tax)
        )
      end
    end
  end
end

================================================
FILE: lib/paypal/payment/recurring/summary.rb
================================================
module Paypal
  module Payment
    class Recurring::Summary < Base
      attr_optional :next_billing_date, :cycles_completed, :cycles_remaining, :outstanding_balance, :failed_count, :last_payment_date, :last_payment_amount

      def numeric_attribute?(key)
        super || [:outstanding_balance, :failed_count].include?(key)
      end
    end
  end
end

================================================
FILE: lib/paypal/payment/recurring.rb
================================================
module Paypal
  module Payment
    class Recurring < Base
      attr_optional :start_date, :description, :identifier, :status, :name, :reference, :max_fails, :auto_bill, :aggregate_amount, :aggregate_optional_amount, :final_payment_date
      attr_accessor :activation, :billing, :regular_billing, :summary

      def initialize(attributes = {})
        super
        @activation = Activation.new attributes[:activation] if attributes[:activation]
        @billing = Billing.new attributes[:billing] if attributes[:billing]
        @regular_billing = Billing.new attributes[:regular_billing] if attributes[:regular_billing]
        @summary = Summary.new attributes[:summary] if attributes[:summary]
      end

      def to_params
        params = [
          self.billing,
          self.activation
        ].compact.inject({}) do |params, attribute|
          params.merge! attribute.to_params
        end
        if self.start_date.is_a?(Time)
          self.start_date = self.start_date.to_s(:db)
        end
        params.merge!(
          :DESC  => self.description,
          :MAXFAILEDPAYMENTS => self.max_fails,
          :AUTOBILLOUTAMT => self.auto_bill,
          :PROFILESTARTDATE => self.start_date,
          :SUBSCRIBERNAME => self.name,
          :PROFILEREFERENCE => self.reference
        )
        params.delete_if do |k, v|
          v.blank?
        end
      end

      def numeric_attribute?(key)
        super || [:max_fails, :failed_count].include?(key)
      end
    end
  end
end

================================================
FILE: lib/paypal/payment/request/item.rb
================================================
module Paypal
  module Payment
    class Request::Item < Base
      attr_optional :name, :description, :amount, :number, :quantity, :category, :url

      def initialize(attributes = {})
        super
        @quantity ||= 1
      end

      def to_params(parent_index, index = 0)
        {
          :"L_PAYMENTREQUEST_#{parent_index}_NAME#{index}" => self.name,
          :"L_PAYMENTREQUEST_#{parent_index}_DESC#{index}" => self.description,
          :"L_PAYMENTREQUEST_#{parent_index}_AMT#{index}" => Util.formatted_amount(self.amount),
          :"L_PAYMENTREQUEST_#{parent_index}_NUMBER#{index}" => self.number,
          :"L_PAYMENTREQUEST_#{parent_index}_QTY#{index}" => self.quantity,
          :"L_PAYMENTREQUEST_#{parent_index}_ITEMCATEGORY#{index}" => self.category,
          :"L_PAYMENTREQUEST_#{parent_index}_ITEMURL#{index}" => self.url
        }.delete_if do |k, v|
          v.blank?
        end
      end
    end
  end
end


================================================
FILE: lib/paypal/payment/request.rb
================================================
module Paypal
  module Payment
    class Request < Base
      attr_optional :action, :currency_code, :description, :notify_url, :billing_type, :billing_agreement_description, :billing_agreement_id, :request_id, :seller_id, :invoice_number, :custom
      attr_accessor :amount, :items, :custom_fields

      def initialize(attributes = {})
        @amount = if attributes[:amount].is_a?(Common::Amount)
          attributes[:amount]
        else
          Common::Amount.new(
            :total => attributes[:amount],
            :tax => attributes[:tax_amount],
            :shipping => attributes[:shipping_amount]
          )
        end
        @items = []
        Array(attributes[:items]).each do |item_attrs|
          @items << Item.new(item_attrs)
        end
        @custom_fields = attributes[:custom_fields] || {}
        super
      end

      def to_params(index = 0)
        params = {
          :"PAYMENTREQUEST_#{index}_PAYMENTACTION" => self.action,
          :"PAYMENTREQUEST_#{index}_AMT" => Util.formatted_amount(self.amount.total),
          :"PAYMENTREQUEST_#{index}_TAXAMT" => Util.formatted_amount(self.amount.tax),
          :"PAYMENTREQUEST_#{index}_SHIPPINGAMT" => Util.formatted_amount(self.amount.shipping),
          :"PAYMENTREQUEST_#{index}_CURRENCYCODE" => self.currency_code,
          :"PAYMENTREQUEST_#{index}_DESC" => self.description,
          :"PAYMENTREQUEST_#{index}_INVNUM" => self.invoice_number,
          :"PAYMENTREQUEST_#{index}_CUSTOM" => self.custom,
          # NOTE:
          #  notify_url works only when DoExpressCheckoutPayment called.
          #  recurring payment doesn't support dynamic notify_url.
          :"PAYMENTREQUEST_#{index}_NOTIFYURL" => self.notify_url,
          :"L_BILLINGTYPE#{index}" => self.billing_type,
          :"L_BILLINGAGREEMENTDESCRIPTION#{index}" => self.billing_agreement_description,
          # FOR PARALLEL PAYMENT
          :"PAYMENTREQUEST_#{index}_PAYMENTREQUESTID" => self.request_id,
          :"PAYMENTREQUEST_#{index}_SELLERPAYPALACCOUNTID" => self.seller_id
        }.delete_if do |k, v|
          v.blank?
        end
        if self.items.present?
          params[:"PAYMENTREQUEST_#{index}_ITEMAMT"] = Util.formatted_amount(self.items_amount)
          self.items.each_with_index do |item, item_index|
            params.merge! item.to_params(index, item_index)
          end
        end
        self.custom_fields.each do |key, value|
          field = key.to_s.upcase.gsub("{N}", index.to_s).to_sym
          params[field] = value
        end
        params
      end

      def items_amount
        self.items.sum do |item|
          item.quantity * BigDecimal.new(item.amount.to_s)
        end
      end
    end
  end
end


================================================
FILE: lib/paypal/payment/response/address.rb
================================================
module Paypal
  module Payment
    class Response::Address < Base
      attr_optional :owner, :status, :name, :zip, :street, :street2, :city, :state, :country_code, :country_name
    end
  end
end

================================================
FILE: lib/paypal/payment/response/info.rb
================================================
module Paypal
  module Payment
    class Response::Info < Base
      cattr_reader :attribute_mapping
      @@attribute_mapping = {
        :ACK => :ack,
        :CURRENCYCODE => :currency_code,
        :ERRORCODE => :error_code,
        :ORDERTIME => :order_time,
        :PAYMENTSTATUS => :payment_status,
        :PAYMENTTYPE => :payment_type,
        :PENDINGREASON => :pending_reason,
        :PROTECTIONELIGIBILITY => :protection_eligibility,
        :PROTECTIONELIGIBILITYTYPE => :protection_eligibility_type,
        :REASONCODE => :reason_code,
        :RECEIPTID => :receipt_id,
        :SECUREMERCHANTACCOUNTID => :secure_merchant_account_id,
        :TRANSACTIONID => :transaction_id,
        :TRANSACTIONTYPE => :transaction_type,
        :PAYMENTREQUESTID => :request_id,
        :SELLERPAYPALACCOUNTID => :seller_id,
        :EXCHANGERATE => :exchange_rate
      }
      attr_accessor *@@attribute_mapping.values
      attr_accessor :amount

      def initialize(attributes = {})
        attrs = attributes.dup
        @@attribute_mapping.each do |key, value|
          self.send "#{value}=", attrs.delete(key)
        end
        @amount = Common::Amount.new(
          :total => attrs.delete(:AMT),
          :fee => attrs.delete(:FEEAMT),
          :tax => attrs.delete(:TAXAMT)
        )

        # warn ignored params
        attrs.each do |key, value|
          Paypal.log "Ignored Parameter (#{self.class}): #{key}=#{value}", :warn
        end
      end
    end
  end
end


================================================
FILE: lib/paypal/payment/response/item.rb
================================================
module Paypal
  module Payment
    class Response::Item < Base
      cattr_reader :attribute_mapping
      @@attribute_mapping = {
        :NAME => :name,
        :DESC => :description,
        :QTY => :quantity,
        :NUMBER => :number,
        :ITEMCATEGORY => :category,
        :ITEMWIDTHVALUE => :width,
        :ITEMHEIGHTVALUE => :height,
        :ITEMLENGTHVALUE => :length,
        :ITEMWEIGHTVALUE => :weight,
        :SHIPPINGAMT => :shipping,
        :HANDLINGAMT => :handling,
        :CURRENCYCODE => :currency
        
      }
      attr_accessor *@@attribute_mapping.values
      attr_accessor :amount

      def initialize(attributes = {})
        attrs = attributes.dup
        @@attribute_mapping.each do |key, value|
          self.send "#{value}=", attrs.delete(key)
        end
        @quantity = @quantity.to_i
        @amount = Common::Amount.new(
          :total => attrs.delete(:AMT),
          :tax => attrs.delete(:TAXAMT)
        )

        # warn ignored params
        attrs.each do |key, value|
          Paypal.log "Ignored Parameter (#{self.class}): #{key}=#{value}", :warn
        end
      end
    end
  end
end


================================================
FILE: lib/paypal/payment/response/payer.rb
================================================
module Paypal
  module Payment
    class Response::Payer < Base
      attr_optional :identifier, :status, :first_name, :last_name, :email, :company
    end
  end
end

================================================
FILE: lib/paypal/payment/response/reference.rb
================================================
module Paypal
  module Payment
    class Response::Reference < Base
      attr_required :identifier
      attr_optional :description, :status
      attr_accessor :info
    end
  end
end

================================================
FILE: lib/paypal/payment/response/refund.rb
================================================
module Paypal
  module Payment
    class Response::Refund < Base
      attr_optional :transaction_id
      attr_accessor :amount

      def initialize(attributes = {})
        super
        @amount = Common::Amount.new(attributes[:amount])
      end
    end
  end
end

================================================
FILE: lib/paypal/payment/response.rb
================================================
module Paypal
  module Payment
    class Response < Base
      attr_accessor :amount, :ship_to, :bill_to, :description, :note, :items, :notify_url, :insurance_option_offered, :currency_code, :short_message, :long_message, :error_code, :severity_code, :ack, :transaction_id, :billing_agreement_id, :request_id, :seller_id

      def initialize(attributes = {})
        attrs = attributes.dup
        @amount = Common::Amount.new(
          :total => attrs.delete(:AMT),
          :item => attrs.delete(:ITEMAMT),
          :handing => attrs.delete(:HANDLINGAMT),
          :insurance => attrs.delete(:INSURANCEAMT),
          :ship_disc => attrs.delete(:SHIPDISCAMT),
          :shipping => attrs.delete(:SHIPPINGAMT),
          :tax => attrs.delete(:TAXAMT)
        )
        @ship_to = Payment::Response::Address.new(
          :name => attrs.delete(:SHIPTONAME),
          :zip => attrs.delete(:SHIPTOZIP),
          :street => attrs.delete(:SHIPTOSTREET),
          :street2 => attrs.delete(:SHIPTOSTREET2),
          :city => attrs.delete(:SHIPTOCITY),
          :state => attrs.delete(:SHIPTOSTATE),
          :country_code => attrs.delete(:SHIPTOCOUNTRYCODE),
          :country_name => attrs.delete(:SHIPTOCOUNTRYNAME)
        )
        @bill_to = Payment::Response::Address.new(
          :owner => attrs.delete(:ADDRESSID),
          :status => attrs.delete(:ADDRESSSTATUS),
          :name => attrs.delete(:BILLINGNAME),
          :zip => attrs.delete(:ZIP),
          :street => attrs.delete(:STREET),
          :street2 => attrs.delete(:STREET2),
          :city => attrs.delete(:CITY),
          :state => attrs.delete(:STATE),
          :country_code => attrs.delete(:COUNTRY)
        )
        @description = attrs.delete(:DESC)
        @note = attrs.delete(:NOTETEXT)
        @notify_url = attrs.delete(:NOTIFYURL)
        @insurance_option_offered = attrs.delete(:INSURANCEOPTIONOFFERED) == 'true'
        @currency_code = attrs.delete(:CURRENCYCODE)
        @short_message = attrs.delete(:SHORTMESSAGE)
        @long_message = attrs.delete(:LONGMESSAGE)
        @error_code = attrs.delete(:ERRORCODE)
        @severity_code = attrs.delete(:SEVERITYCODE)
        @ack = attrs.delete(:ACK)
        @transaction_id = attrs.delete(:TRANSACTIONID)
        @billing_agreement_id = attrs.delete(:BILLINGAGREEMENTID)
        @request_id = attrs.delete(:PAYMENTREQUESTID)
        @seller_id = attrs.delete(:SELLERPAYPALACCOUNTID)

        # items
        items = []
        attrs.keys.each do |_attr_|
          key, index = _attr_.to_s.scan(/^(.+?)(\d+)$/).flatten
          if index
            items[index.to_i] ||= {}
            items[index.to_i][key.to_sym] = attrs.delete(:"#{key}#{index}")
          end
        end
        @items = items.collect do |_attr_|
          Item.new(_attr_)
        end

        # warn ignored params
        attrs.each do |key, value|
          Paypal.log "Ignored Parameter (#{self.class}): #{key}=#{value}", :warn
        end
      end
    end
  end
end


================================================
FILE: lib/paypal/util.rb
================================================
module Paypal
  module Util

    def self.formatted_amount(x)
      # Thanks @nahi ;)
      sprintf "%0.2f", BigDecimal.new(x.to_s).truncate(2)
    end

    def self.to_numeric(x)
      if x.to_f == x.to_i
        x.to_i
      else
        x.to_f
      end
    end

    def numeric_attribute?(key)
      !!(key.to_s =~ /(amount|frequency|cycles|paid)/)
    end

    def ==(other)
      instance_variables.all? do |key|
        instance_variable_get(key) == other.instance_variable_get(key)
      end
    end

  end
end

================================================
FILE: lib/paypal.rb
================================================
require 'logger'
require 'active_support'
require 'active_support/core_ext'
require 'attr_required'
require 'attr_optional'
require 'rest_client'

module Paypal
  mattr_accessor :api_version
  self.api_version = '88.0'

  ENDPOINT = {
    :production => 'https://www.paypal.com/cgi-bin/webscr',
    :sandbox => 'https://www.sandbox.paypal.com/cgi-bin/webscr'
  }
  POPUP_ENDPOINT = {
    :production => 'https://www.paypal.com/incontext',
    :sandbox => 'https://www.sandbox.paypal.com/incontext'
  }

  def self.endpoint
    if sandbox?
      Paypal::ENDPOINT[:sandbox]
    else
      Paypal::ENDPOINT[:production]
    end
  end
  def self.popup_endpoint
    if sandbox?
      Paypal::POPUP_ENDPOINT[:sandbox]
    else
      Paypal::POPUP_ENDPOINT[:production]
    end
  end

  def self.log(message, mode = :info)
    self.logger.send mode, message
  end
  def self.logger
    @@logger
  end
  def self.logger=(logger)
    @@logger = logger
  end
  @@logger = Logger.new(STDERR)
  @@logger.progname = 'Paypal::Express'

  def self.sandbox?
    @@sandbox
  end
  def self.sandbox!
    self.sandbox = true
  end
  def self.sandbox=(boolean)
    @@sandbox = boolean
  end
  self.sandbox = false

end

require 'paypal/util'
require 'paypal/exception'
require 'paypal/exception/http_error'
require 'paypal/exception/api_error'
require 'paypal/base'
require 'paypal/ipn'
require 'paypal/nvp/request'
require 'paypal/nvp/response'
require 'paypal/payment/common/amount'
require 'paypal/express/request'
require 'paypal/express/response'
require 'paypal/payment/request'
require 'paypal/payment/request/item'
require 'paypal/payment/response'
require 'paypal/payment/response/info'
require 'paypal/payment/response/item'
require 'paypal/payment/response/payer'
require 'paypal/payment/response/reference'
require 'paypal/payment/response/refund'
require 'paypal/payment/response/address'
require 'paypal/payment/recurring'
require 'paypal/payment/recurring/activation'
require 'paypal/payment/recurring/billing'
require 'paypal/payment/recurring/summary'


================================================
FILE: paypal-express.gemspec
================================================
Gem::Specification.new do |s|
  s.name = "paypal-express"
  s.version = File.read(File.join(File.dirname(__FILE__), "VERSION"))
  s.required_rubygems_version = Gem::Requirement.new(">= 1.3.6") if s.respond_to? :required_rubygems_version=
  s.authors = ["nov matake"]
  s.description = %q{PayPal Express Checkout API Client for Instance, Recurring and Digital Goods Payment.}
  s.summary = %q{PayPal Express Checkout API Client for Instance, Recurring and Digital Goods Payment.}
  s.email = "nov@matake.jp"
  s.extra_rdoc_files = ["LICENSE", "README.rdoc"]
  s.rdoc_options = ["--charset=UTF-8"]
  s.homepage = "http://github.com/nov/paypal-express"
  s.require_paths = ["lib"]
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
  s.files = `git ls-files`.split("\n")
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
  s.add_dependency "activesupport", ">= 2.3"
  s.add_dependency "rest-client"
  s.add_dependency "attr_required", ">= 0.0.5"
  s.add_development_dependency "rake", ">= 0.8"
  s.add_development_dependency "simplecov"
  s.add_development_dependency "rspec", "< 2.99"
  s.add_development_dependency "fakeweb", ">= 1.3.0"
end


================================================
FILE: spec/fake_response/BillAgreementUpdate/fetch.txt
================================================
BILLINGAGREEMENTID=B%2d8XW15926RT2253736&BILLINGAGREEMENTDESCRIPTION=Reference%20Payment%20Agreement&BILLINGAGREEMENTSTATUS=Canceled&TIMESTAMP=2011%2d08%2d06T09%3a47%3a41Z&CORRELATIONID=28e5a24b3ea98&ACK=Success&VERSION=78%2e0&BUILD=2020243&EMAIL=client_1305381804_per%40matake%2ejp&PAYERID=CMJLWADKVFQYJ&PAYERSTATUS=verified&FIRSTNAME=Test&LASTNAME=User&COUNTRYCODE=US

================================================
FILE: spec/fake_response/BillAgreementUpdate/revoke.txt
================================================
BILLINGAGREEMENTID=B%2d8XW15926RT2253736&BILLINGAGREEMENTDESCRIPTION=Reference%20Payment%20Agreement&BILLINGAGREEMENTSTATUS=Canceled&TIMESTAMP=2011%2d08%2d06T09%3a47%3a41Z&CORRELATIONID=28e5a24b3ea98&ACK=Success&VERSION=78%2e0&BUILD=2020243&EMAIL=client_1305381804_per%40matake%2ejp&PAYERID=CMJLWADKVFQYJ&PAYERSTATUS=verified&FIRSTNAME=Test&LASTNAME=User&COUNTRYCODE=US

================================================
FILE: spec/fake_response/CreateBillingAgreement/success.txt
================================================
BILLINGAGREEMENTID=B%2d45776062X95244343&TIMESTAMP=2011%2d08%2d06T07%3a53%3a50Z&CORRELATIONID=9a16fde4456e1&ACK=Success&VERSION=78%2e0&BUILD=2020243

================================================
FILE: spec/fake_response/CreateRecurringPaymentsProfile/failure.txt
================================================
TIMESTAMP=2011%2d02%2d08T07%3a18%3a29Z&CORRELATIONID=fde1527b25b4f&ACK=Failure&VERSION=66%2e0&BUILD=1704252&L_ERRORCODE0=11502&L_SHORTMESSAGE0=Invalid%20Token&L_LONGMESSAGE0=The%20token%20is%20invalid&L_SEVERITYCODE0=Error

================================================
FILE: spec/fake_response/CreateRecurringPaymentsProfile/success.txt
================================================
PROFILEID=I%2dL8N58XFUCET3&PROFILESTATUS=ActiveProfile&TIMESTAMP=2011%2d02%2d08T07%3a17%3a51Z&CORRELATIONID=7d2e125f5ca63&ACK=Success&VERSION=66%2e0&BUILD=1704252

================================================
FILE: spec/fake_response/DoCapture/failure.txt
================================================
ACK=Failure&BUILD=8334781&CORRELATIONID=dd9e16358819b&L_ERRORCODE0=10609&L_LONGMESSAGE0=Transaction+id+is+invalid.&L_SEVERITYCODE0=Error&L_SHORTMESSAGE0=Invalid+transactionID.&TIMESTAMP=2013-11-05T17%3A43%3A56Z&VERSION=88.0


================================================
FILE: spec/fake_response/DoCapture/success.txt
================================================
ACK=Success&AMT=440.00&AUTHORIZATIONID=2RG78938NK8989844&BUILD=8334781&CORRELATIONID=311611c111b48&CURRENCYCODE=BRL&EXCHANGERATE=0.426354&FEEAMT=13.16&ORDERTIME=2013-11-05T17%3A34%3A56Z&PARENTTRANSACTIONID=2RG78938NK8989844&PAYMENTSTATUS=Completed&PAYMENTTYPE=instant&PENDINGREASON=None&PROTECTIONELIGIBILITY=Ineligible&PROTECTIONELIGIBILITYTYPE=None&REASONCODE=None&SETTLEAMT=181.98&TAXAMT=0.00&TIMESTAMP=2013-11-05T17%3A34%3A56Z&TRANSACTIONID=9VW4495267708531S&TRANSACTIONTYPE=expresscheckout&VERSION=88.0


================================================
FILE: spec/fake_response/DoExpressCheckoutPayment/failure.txt
================================================
TIMESTAMP=2011%2d02%2d08T07%3a15%3a19Z&CORRELATIONID=5dd0308345382&ACK=Failure&VERSION=66%2e0&BUILD=1721431&L_ERRORCODE0=10410&L_SHORTMESSAGE0=Invalid%20token&L_LONGMESSAGE0=Invalid%20token%2e&L_SEVERITYCODE0=Error

================================================
FILE: spec/fake_response/DoExpressCheckoutPayment/success.txt
================================================
TOKEN=EC-9E2743126S4330617&SUCCESSPAGEREDIRECTREQUESTED=false&TIMESTAMP=2011-02-08T03:23:55Z&CORRELATIONID=15b93874c358c&ACK=Success&VERSION=66.0&BUILD=1721431&INSURANCEOPTIONSELECTED=false&SHIPPINGOPTIONISDEFAULT=false&PAYMENTINFO_0_TRANSACTIONID=8NC65222871997739&PAYMENTINFO_0_TRANSACTIONTYPE=expresscheckout&PAYMENTINFO_0_PAYMENTTYPE=instant&PAYMENTINFO_0_ORDERTIME=2011-02-08T03:23:54Z&PAYMENTINFO_0_AMT=14.00&PAYMENTINFO_0_FEEAMT=0.85&PAYMENTINFO_0_TAXAMT=0.00&PAYMENTINFO_0_CURRENCYCODE=USD&PAYMENTINFO_0_PAYMENTSTATUS=Completed&PAYMENTINFO_0_PENDINGREASON=None&PAYMENTINFO_0_REASONCODE=None&PAYMENTINFO_0_PROTECTIONELIGIBILITY=Ineligible&PAYMENTINFO_0_PROTECTIONELIGIBILITYTYPE=None&PAYMENTINFO_0_ERRORCODE=0&PAYMENTINFO_0_ACK=Success

================================================
FILE: spec/fake_response/DoExpressCheckoutPayment/success_with_billing_agreement.txt
================================================
TOKEN=EC-9E2743126S4330617&SUCCESSPAGEREDIRECTREQUESTED=false&TIMESTAMP=2011-02-08T03:23:55Z&CORRELATIONID=15b93874c358c&ACK=Success&VERSION=66.0&BUILD=1721431&INSURANCEOPTIONSELECTED=false&SHIPPINGOPTIONISDEFAULT=false&PAYMENTINFO_0_TRANSACTIONID=8NC65222871997739&PAYMENTINFO_0_TRANSACTIONTYPE=expresscheckout&PAYMENTINFO_0_PAYMENTTYPE=instant&PAYMENTINFO_0_ORDERTIME=2011-02-08T03:23:54Z&PAYMENTINFO_0_AMT=14.00&PAYMENTINFO_0_FEEAMT=0.85&PAYMENTINFO_0_TAXAMT=0.00&PAYMENTINFO_0_CURRENCYCODE=USD&PAYMENTINFO_0_PAYMENTSTATUS=Completed&PAYMENTINFO_0_PENDINGREASON=None&PAYMENTINFO_0_REASONCODE=None&PAYMENTINFO_0_PROTECTIONELIGIBILITY=Ineligible&PAYMENTINFO_0_PROTECTIONELIGIBILITYTYPE=None&PAYMENTINFO_0_ERRORCODE=0&BILLINGAGREEMENTID=B-1XR87946TC504770W&PAYMENTINFO_0_ACK=Success

================================================
FILE: spec/fake_response/DoExpressCheckoutPayment/success_with_many_items.txt
================================================
TOKEN=EC%2d7HG122968U843864S&CHECKOUTSTATUS=PaymentActionNotInitiated&TIMESTAMP=2011%2d04%2d12T03%3a51%3a58Z&CORRELATIONID=6a0a92ea79ac3&ACK=Success&VERSION=69%2e0&BUILD=1824201&EMAIL=buyer_1297999636_per%40cerego%2ecom&PAYERID=9RWDTMRKKHQ8S&PAYERSTATUS=verified&FIRSTNAME=Test&LASTNAME=User&COUNTRYCODE=US&CURRENCYCODE=USD&AMT=1000&ITEMAMT=1000&SHIPPINGAMT=0&HANDLINGAMT=0&TAXAMT=0&DESC=Instant%20Payment%20Request&NOTIFYURL=http%3a%2f%2fmerchant%2eexample%2ecom%2fnotify&INSURANCEAMT=0&SHIPDISCAMT=0&L_ITEMHEIGHTVALUE0=%20%20%200%2e00000&L_ITEMHEIGHTVALUE1=%20%20%200%2e00000&L_ITEMHEIGHTVALUE2=%20%20%200%2e00000&L_ITEMHEIGHTVALUE3=%20%20%200%2e00000&L_ITEMHEIGHTVALUE4=%20%20%200%2e00000&L_ITEMHEIGHTVALUE5=%20%20%200%2e00000&L_ITEMHEIGHTVALUE6=%20%20%200%2e00000&L_ITEMHEIGHTVALUE7=%20%20%200%2e00000&L_ITEMHEIGHTVALUE8=%20%20%200%2e00000&L_ITEMHEIGHTVALUE9=%20%20%200%2e00000&L_ITEMHEIGHTVALUE10=%20%20%200%2e00000&L_ITEMHEIGHTVALUE11=%20%20%200%2e00000&L_ITEMHEIGHTVALUE12=%20%20%200%2e00000&L_ITEMHEIGHTVALUE13=%20%20%200%2e00000&L_ITEMHEIGHTVALUE14=%20%20%200%2e00000&L_ITEMHEIGHTVALUE15=%20%20%200%2e00000&L_ITEMHEIGHTVALUE16=%20%20%200%2e00000&L_ITEMHEIGHTVALUE17=%20%20%200%2e00000&L_ITEMHEIGHTVALUE18=%20%20%200%2e00000&L_ITEMHEIGHTVALUE19=%20%20%200%2e00000&L_ITEMWIDTHVALUE0=%20%20%200%2e00000&L_ITEMWIDTHVALUE1=%20%20%200%2e00000&L_ITEMWIDTHVALUE2=%20%20%200%2e00000&L_ITEMWIDTHVALUE3=%20%20%200%2e00000&L_ITEMWIDTHVALUE4=%20%20%200%2e00000&L_ITEMWIDTHVALUE5=%20%20%200%2e00000&L_ITEMWIDTHVALUE6=%20%20%200%2e00000&L_ITEMWIDTHVALUE7=%20%20%200%2e00000&L_ITEMWIDTHVALUE8=%20%20%200%2e00000&L_ITEMWIDTHVALUE9=%20%20%200%2e00000&L_ITEMWIDTHVALUE10=%20%20%200%2e00000&L_ITEMWIDTHVALUE11=%20%20%200%2e00000&L_ITEMWIDTHVALUE12=%20%20%200%2e00000&L_ITEMWIDTHVALUE13=%20%20%200%2e00000&L_ITEMWIDTHVALUE14=%20%20%200%2e00000&L_ITEMWIDTHVALUE15=%20%20%200%2e00000&L_ITEMWIDTHVALUE16=%20%20%200%2e00000&L_ITEMWIDTHVALUE17=%20%20%200%2e00000&L_ITEMWIDTHVALUE18=%20%20%200%2e00000&L_ITEMWIDTHVALUE19=%20%20%200%2e00000&L_ITEMLENGTHVALUE0=%20%20%200%2e00000&L_ITEMLENGTHVALUE1=%20%20%200%2e00000&L_ITEMLENGTHVALUE2=%20%20%200%2e00000&L_ITEMLENGTHVALUE3=%20%20%200%2e00000&L_ITEMLENGTHVALUE4=%20%20%200%2e00000&L_ITEMLENGTHVALUE5=%20%20%200%2e00000&L_ITEMLENGTHVALUE6=%20%20%200%2e00000&L_ITEMLENGTHVALUE7=%20%20%200%2e00000&L_ITEMLENGTHVALUE8=%20%20%200%2e00000&L_ITEMLENGTHVALUE9=%20%20%200%2e00000&L_ITEMLENGTHVALUE10=%20%20%200%2e00000&L_ITEMLENGTHVALUE11=%20%20%200%2e00000&L_ITEMLENGTHVALUE12=%20%20%200%2e00000&L_ITEMLENGTHVALUE13=%20%20%200%2e00000&L_ITEMLENGTHVALUE14=%20%20%200%2e00000&L_ITEMLENGTHVALUE15=%20%20%200%2e00000&L_ITEMLENGTHVALUE16=%20%20%200%2e00000&L_ITEMLENGTHVALUE17=%20%20%200%2e00000&L_ITEMLENGTHVALUE18=%20%20%200%2e00000&L_ITEMLENGTHVALUE19=%20%20%200%2e00000&L_NAME0=Item0&L_NAME1=Item1&L_NAME2=Item2&L_NAME3=Item3&L_NAME4=Item4&L_NAME5=Item5&L_NAME6=Item6&L_NAME7=Item7&L_NAME8=Item8&L_NAME9=Item9&L_NAME10=Item10&L_NAME11=Item11&L_NAME12=Item12&L_NAME13=Item13&L_NAME14=Item14&L_NAME15=Item15&L_NAME16=Item16&L_NAME17=Item17&L_NAME18=Item18&L_NAME19=Item19&L_ITEMWEIGHTVALUE0=%20%20%200%2e00000&L_ITEMWEIGHTVALUE1=%20%20%200%2e00000&L_ITEMWEIGHTVALUE2=%20%20%200%2e00000&L_ITEMWEIGHTVALUE3=%20%20%200%2e00000&L_ITEMWEIGHTVALUE4=%20%20%200%2e00000&L_ITEMWEIGHTVALUE5=%20%20%200%2e00000&L_ITEMWEIGHTVALUE6=%20%20%200%2e00000&L_ITEMWEIGHTVALUE7=%20%20%200%2e00000&L_ITEMWEIGHTVALUE8=%20%20%200%2e00000&L_ITEMWEIGHTVALUE9=%20%20%200%2e00000&L_ITEMWEIGHTVALUE10=%20%20%200%2e00000&L_ITEMWEIGHTVALUE11=%20%20%200%2e00000&L_ITEMWEIGHTVALUE12=%20%20%200%2e00000&L_ITEMWEIGHTVALUE13=%20%20%200%2e00000&L_ITEMWEIGHTVALUE14=%20%20%200%2e00000&L_ITEMWEIGHTVALUE15=%20%20%200%2e00000&L_ITEMWEIGHTVALUE16=%20%20%200%2e00000&L_ITEMWEIGHTVALUE17=%20%20%200%2e00000&L_ITEMWEIGHTVALUE18=%20%20%200%2e00000&L_ITEMWEIGHTVALUE19=%20%20%200%2e00000&L_TAXAMT0=%20%20%200%2e00000&L_TAXAMT1=%20%20%200%2e00000&L_TAXAMT2=%20%20%200%2e00000&L_TAXAMT3=%20%20%200%2e00000&L_TAXAMT4=%20%20%200%2e00000&L_TAXAMT5=%20%20%200%2e00000&L_TAXAMT6=%20%20%200%2e00000&L_TAXAMT7=%20%20%200%2e00000&L_TAXAMT8=%20%20%200%2e00000&L_TAXAMT9=%20%20%200%2e00000&L_TAXAMT10=%20%20%200%2e00000&L_TAXAMT11=%20%20%200%2e00000&L_TAXAMT12=%20%20%200%2e00000&L_TAXAMT13=%20%20%200%2e00000&L_TAXAMT14=%20%20%200%2e00000&L_TAXAMT15=%20%20%200%2e00000&L_TAXAMT16=%20%20%200%2e00000&L_TAXAMT17=%20%20%200%2e00000&L_TAXAMT18=%20%20%200%2e00000&L_TAXAMT19=%20%20%200%2e00000&L_DESC0=A%20new%20Item%200&L_DESC1=A%20new%20Item%201&L_DESC2=A%20new%20Item%202&L_DESC3=A%20new%20Item%203&L_DESC4=A%20new%20Item%204&L_DESC5=A%20new%20Item%205&L_DESC6=A%20new%20Item%206&L_DESC7=A%20new%20Item%207&L_DESC8=A%20new%20Item%208&L_DESC9=A%20new%20Item%209&L_DESC10=A%20new%20Item%2010&L_DESC11=A%20new%20Item%2011&L_DESC12=A%20new%20Item%2012&L_DESC13=A%20new%20Item%2013&L_DESC14=A%20new%20Item%2014&L_DESC15=A%20new%20Item%2015&L_DESC16=A%20new%20Item%2016&L_DESC17=A%20new%20Item%2017&L_DESC18=A%20new%20Item%2018&L_DESC19=A%20new%20Item%2019&L_AMT0=50&L_AMT1=50&L_AMT2=50&L_AMT3=50&L_AMT4=50&L_AMT5=50&L_AMT6=50&L_AMT7=50&L_AMT8=50&L_AMT9=50&L_AMT10=50&L_AMT11=50&L_AMT12=50&L_AMT13=50&L_AMT14=50&L_AMT15=50&L_AMT16=50&L_AMT17=50&L_AMT18=50&L_AMT19=50&L_QTY0=1&L_QTY1=1&L_QTY2=1&L_QTY3=1&L_QTY4=1&L_QTY5=1&L_QTY6=1&L_QTY7=1&L_QTY8=1&L_QTY9=1&L_QTY10=1&L_QTY11=1&L_QTY12=1&L_QTY13=1&L_QTY14=1&L_QTY15=1&L_QTY16=1&L_QTY17=1&L_QTY18=1&L_QTY19=1&PAYMENTREQUEST_0_CURRENCYCODE=JPY&PAYMENTREQUEST_0_AMT=10&PAYMENTREQUEST_0_ITEMAMT=10&PAYMENTREQUEST_0_SHIPPINGAMT=0&PAYMENTREQUEST_0_HANDLINGAMT=0&PAYMENTREQUEST_0_TAXAMT=0&PAYMENTREQUEST_0_DESC=Instant%20Payment%20Request&PAYMENTREQUEST_0_NOTIFYURL=http%3a%2f%2fmerchant%2eexample%2ecom%2fnotify&PAYMENTREQUEST_0_INSURANCEAMT=0&PAYMENTREQUEST_0_SHIPDISCAMT=0&PAYMENTREQUEST_0_INSURANCEOPTIONOFFERED=false&L_PAYMENTREQUEST_0_ITEMHEIGHTVALUE0=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMHEIGHTVALUE1=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMHEIGHTVALUE2=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMHEIGHTVALUE3=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMHEIGHTVALUE4=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMHEIGHTVALUE5=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMHEIGHTVALUE6=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMHEIGHTVALUE7=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMHEIGHTVALUE8=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMHEIGHTVALUE9=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMHEIGHTVALUE10=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMHEIGHTVALUE11=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMHEIGHTVALUE12=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMHEIGHTVALUE13=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMHEIGHTVALUE14=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMHEIGHTVALUE15=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMHEIGHTVALUE16=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMHEIGHTVALUE17=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMHEIGHTVALUE18=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMHEIGHTVALUE19=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWIDTHVALUE0=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWIDTHVALUE1=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWIDTHVALUE2=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWIDTHVALUE3=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWIDTHVALUE4=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWIDTHVALUE5=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWIDTHVALUE6=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWIDTHVALUE7=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWIDTHVALUE8=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWIDTHVALUE9=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWIDTHVALUE10=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWIDTHVALUE11=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWIDTHVALUE12=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWIDTHVALUE13=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWIDTHVALUE14=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWIDTHVALUE15=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWIDTHVALUE16=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWIDTHVALUE17=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWIDTHVALUE18=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWIDTHVALUE19=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMLENGTHVALUE0=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMLENGTHVALUE1=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMLENGTHVALUE2=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMLENGTHVALUE3=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMLENGTHVALUE4=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMLENGTHVALUE5=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMLENGTHVALUE6=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMLENGTHVALUE7=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMLENGTHVALUE8=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMLENGTHVALUE9=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMLENGTHVALUE10=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMLENGTHVALUE11=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMLENGTHVALUE12=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMLENGTHVALUE13=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMLENGTHVALUE14=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMLENGTHVALUE15=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMLENGTHVALUE16=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMLENGTHVALUE17=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMLENGTHVALUE18=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMLENGTHVALUE19=%20%20%200%2e00000&L_PAYMENTREQUEST_0_NAME0=Item0&L_PAYMENTREQUEST_0_NAME1=Item1&L_PAYMENTREQUEST_0_NAME2=Item2&L_PAYMENTREQUEST_0_NAME3=Item3&L_PAYMENTREQUEST_0_NAME4=Item4&L_PAYMENTREQUEST_0_NAME5=Item5&L_PAYMENTREQUEST_0_NAME6=Item6&L_PAYMENTREQUEST_0_NAME7=Item7&L_PAYMENTREQUEST_0_NAME8=Item8&L_PAYMENTREQUEST_0_NAME9=Item9&L_PAYMENTREQUEST_0_NAME10=Item10&L_PAYMENTREQUEST_0_NAME11=Item11&L_PAYMENTREQUEST_0_NAME12=Item12&L_PAYMENTREQUEST_0_NAME13=Item13&L_PAYMENTREQUEST_0_NAME14=Item14&L_PAYMENTREQUEST_0_NAME15=Item15&L_PAYMENTREQUEST_0_NAME16=Item16&L_PAYMENTREQUEST_0_NAME17=Item17&L_PAYMENTREQUEST_0_NAME18=Item18&L_PAYMENTREQUEST_0_NAME19=Item19&L_PAYMENTREQUEST_0_ITEMWEIGHTVALUE0=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWEIGHTVALUE1=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWEIGHTVALUE2=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWEIGHTVALUE3=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWEIGHTVALUE4=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWEIGHTVALUE5=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWEIGHTVALUE6=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWEIGHTVALUE7=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWEIGHTVALUE8=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWEIGHTVALUE9=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWEIGHTVALUE10=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWEIGHTVALUE11=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWEIGHTVALUE12=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWEIGHTVALUE13=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWEIGHTVALUE14=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWEIGHTVALUE15=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWEIGHTVALUE16=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWEIGHTVALUE17=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWEIGHTVALUE18=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWEIGHTVALUE19=%20%20%200%2e00000&L_PAYMENTREQUEST_0_TAXAMT0=%20%20%200%2e00000&L_PAYMENTREQUEST_0_TAXAMT1=%20%20%200%2e00000&L_PAYMENTREQUEST_0_TAXAMT2=%20%20%200%2e00000&L_PAYMENTREQUEST_0_TAXAMT3=%20%20%200%2e00000&L_PAYMENTREQUEST_0_TAXAMT4=%20%20%200%2e00000&L_PAYMENTREQUEST_0_TAXAMT5=%20%20%200%2e00000&L_PAYMENTREQUEST_0_TAXAMT6=%20%20%200%2e00000&L_PAYMENTREQUEST_0_TAXAMT7=%20%20%200%2e00000&L_PAYMENTREQUEST_0_TAXAMT8=%20%20%200%2e00000&L_PAYMENTREQUEST_0_TAXAMT9=%20%20%200%2e00000&L_PAYMENTREQUEST_0_TAXAMT10=%20%20%200%2e00000&L_PAYMENTREQUEST_0_TAXAMT11=%20%20%200%2e00000&L_PAYMENTREQUEST_0_TAXAMT12=%20%20%200%2e00000&L_PAYMENTREQUEST_0_TAXAMT13=%20%20%200%2e00000&L_PAYMENTREQUEST_0_TAXAMT14=%20%20%200%2e00000&L_PAYMENTREQUEST_0_TAXAMT15=%20%20%200%2e00000&L_PAYMENTREQUEST_0_TAXAMT16=%20%20%200%2e00000&L_PAYMENTREQUEST_0_TAXAMT17=%20%20%200%2e00000&L_PAYMENTREQUEST_0_TAXAMT18=%20%20%200%2e00000&L_PAYMENTREQUEST_0_TAXAMT19=%20%20%200%2e00000&L_PAYMENTREQUEST_0_DESC0=A%20new%20Item%200&L_PAYMENTREQUEST_0_DESC1=A%20new%20Item%201&L_PAYMENTREQUEST_0_DESC2=A%20new%20Item%202&L_PAYMENTREQUEST_0_DESC3=A%20new%20Item%203&L_PAYMENTREQUEST_0_DESC4=A%20new%20Item%204&L_PAYMENTREQUEST_0_DESC5=A%20new%20Item%205&L_PAYMENTREQUEST_0_DESC6=A%20new%20Item%206&L_PAYMENTREQUEST_0_DESC7=A%20new%20Item%207&L_PAYMENTREQUEST_0_DESC8=A%20new%20Item%208&L_PAYMENTREQUEST_0_DESC9=A%20new%20Item%209&L_PAYMENTREQUEST_0_DESC10=A%20new%20Item%2010&L_PAYMENTREQUEST_0_DESC11=A%20new%20Item%2011&L_PAYMENTREQUEST_0_DESC12=A%20new%20Item%2012&L_PAYMENTREQUEST_0_DESC13=A%20new%20Item%2013&L_PAYMENTREQUEST_0_DESC14=A%20new%20Item%2014&L_PAYMENTREQUEST_0_DESC15=A%20new%20Item%2015&L_PAYMENTREQUEST_0_DESC16=A%20new%20Item%2016&L_PAYMENTREQUEST_0_DESC17=A%20new%20Item%2017&L_PAYMENTREQUEST_0_DESC18=A%20new%20Item%2018&L_PAYMENTREQUEST_0_DESC19=A%20new%20Item%2019&L_PAYMENTREQUEST_0_AMT0=50&L_PAYMENTREQUEST_0_AMT1=50&L_PAYMENTREQUEST_0_AMT2=50&L_PAYMENTREQUEST_0_AMT3=50&L_PAYMENTREQUEST_0_AMT4=50&L_PAYMENTREQUEST_0_AMT5=50&L_PAYMENTREQUEST_0_AMT6=50&L_PAYMENTREQUEST_0_AMT7=50&L_PAYMENTREQUEST_0_AMT8=50&L_PAYMENTREQUEST_0_AMT9=50&L_PAYMENTREQUEST_0_AMT10=50&L_PAYMENTREQUEST_0_AMT11=50&L_PAYMENTREQUEST_0_AMT12=50&L_PAYMENTREQUEST_0_AMT13=50&L_PAYMENTREQUEST_0_AMT14=50&L_PAYMENTREQUEST_0_AMT15=50&L_PAYMENTREQUEST_0_AMT16=50&L_PAYMENTREQUEST_0_AMT17=50&L_PAYMENTREQUEST_0_AMT18=50&L_PAYMENTREQUEST_0_AMT19=50&L_PAYMENTREQUEST_0_QTY0=1&L_PAYMENTREQUEST_0_QTY1=1&L_PAYMENTREQUEST_0_QTY2=1&L_PAYMENTREQUEST_0_QTY3=1&L_PAYMENTREQUEST_0_QTY4=1&L_PAYMENTREQUEST_0_QTY5=1&L_PAYMENTREQUEST_0_QTY6=1&L_PAYMENTREQUEST_0_QTY7=1&L_PAYMENTREQUEST_0_QTY8=1&L_PAYMENTREQUEST_0_QTY9=1&L_PAYMENTREQUEST_0_QTY10=1&L_PAYMENTREQUEST_0_QTY11=1&L_PAYMENTREQUEST_0_QTY12=1&L_PAYMENTREQUEST_0_QTY13=1&L_PAYMENTREQUEST_0_QTY14=1&L_PAYMENTREQUEST_0_QTY15=1&L_PAYMENTREQUEST_0_QTY16=1&L_PAYMENTREQUEST_0_QTY17=1&L_PAYMENTREQUEST_0_QTY18=1&L_PAYMENTREQUEST_0_QTY19=1&PAYMENTREQUESTINFO_0_ERRORCODE=0

================================================
FILE: spec/fake_response/DoReferenceTransaction/failure.txt
================================================
TIMESTAMP=2011%2d07%2d19T08%3a59%3a52Z&CORRELATIONID=ed8096a3c3d7d&ACK=Failure&VERSION=72%2e0&BUILD=1982348&L_ERRORCODE0=11451&L_SHORTMESSAGE0=Billing%20Agreement%20Id%20or%20transaction%20Id%20is%20not%20valid&L_LONGMESSAGE0=Billing%20Agreement%20Id%20or%20transaction%20Id%20is%20not%20valid&L_SEVERITYCODE0=Error&TRANSACTIONTYPE=None&PAYMENTTYPE=None&ORDERTIME=1970%2d01%2d01T00%3a00%3a00Z&PAYMENTSTATUS=None&PENDINGREASON=None&REASONCODE=None


================================================
FILE: spec/fake_response/DoReferenceTransaction/success.txt
================================================
BILLINGAGREEMENTID=B%2d7HU5U5U1ULU14U49E&TIMESTAMP=2011%2d07%2d19T08%3a30%3a08Z&CORRELATIONID=12ca85fb3c9d2&ACK=Success&VERSION=72%2e0&BUILD=1982348&TRANSACTIONID=3IV51111LY1211907&TRANSACTIONTYPE=merchtpmt&PAYMENTTYPE=instant&ORDERTIME=2011%2d07%2d19T08%3a30%3a06Z&AMT=192&FEEAMT=47&TAXAMT=0&CURRENCYCODE=JPY&PAYMENTSTATUS=Completed&PENDINGREASON=None&REASONCODE=None&PROTECTIONELIGIBILITY=Ineligible&PROTECTIONELIGIBILITYTYPE=None


================================================
FILE: spec/fake_response/DoVoid/success.txt
================================================
ACK=Success&AUTHORIZATIONID=1a2b3c4d5e6f&BUILD=13055236&TIMESTAMP=2014-10-03T17%3A34%3A56Z

================================================
FILE: spec/fake_response/GetExpressCheckoutDetails/failure.txt
================================================
TOKEN=EC%2d9E2743126S4330617&CHECKOUTSTATUS=PaymentActionCompleted&TIMESTAMP=2011%2d02%2d08T07%3a10%3a26Z&CORRELATIONID=8f5fddf13ed10&ACK=Failure&VERSION=66%2e0&BUILD=1721431&L_ERRORCODE0=10411&L_SHORTMESSAGE0=This%20Express%20Checkout%20session%20has%20expired%2e&L_LONGMESSAGE0=This%20Express%20Checkout%20session%20has%20expired%2e%20%20Token%20value%20is%20no%20longer%20valid%2e&L_SEVERITYCODE0=Error&EMAIL=valid_1296805890_per%40matake%2ejp&PAYERID=PRT3TZ6MCBCNC&PAYERSTATUS=verified&FIRSTNAME=Test&LASTNAME=User&COUNTRYCODE=US&SHIPTONAME=Test%20User&SHIPTOSTREET=1%20Main%20St&SHIPTOCITY=San%20Jose&SHIPTOSTATE=CA&SHIPTOZIP=95131&SHIPTOCOUNTRYCODE=US&SHIPTOCOUNTRYNAME=United%20States&ADDRESSSTATUS=Confirmed&CURRENCYCODE=USD&AMT=14%2e00&SHIPPINGAMT=0%2e00&HANDLINGAMT=0%2e00&TAXAMT=0%2e00&INSURANCEAMT=0%2e00&SHIPDISCAMT=0%2e00&PAYMENTREQUEST_0_CURRENCYCODE=USD&PAYMENTREQUEST_0_AMT=14%2e00&PAYMENTREQUEST_0_SHIPPINGAMT=0%2e00&PAYMENTREQUEST_0_HANDLINGAMT=0%2e00&PAYMENTREQUEST_0_TAXAMT=0%2e00&PAYMENTREQUEST_0_INSURANCEAMT=0%2e00&PAYMENTREQUEST_0_SHIPDISCAMT=0%2e00&PAYMENTREQUEST_0_TRANSACTIONID=8NC65222871997739&PAYMENTREQUEST_0_INSURANCEOPTIONOFFERED=false&PAYMENTREQUEST_0_SHIPTONAME=Test%20User&PAYMENTREQUEST_0_SHIPTOSTREET=1%20Main%20St&PAYMENTREQUEST_0_SHIPTOCITY=San%20Jose&PAYMENTREQUEST_0_SHIPTOSTATE=CA&PAYMENTREQUEST_0_SHIPTOZIP=95131&PAYMENTREQUEST_0_SHIPTOCOUNTRYCODE=US&PAYMENTREQUEST_0_SHIPTOCOUNTRYNAME=United%20States&PAYMENTREQUESTINFO_0_TRANSACTIONID=8NC65222871997739&PAYMENTREQUESTINFO_0_ERRORCODE=0

================================================
FILE: spec/fake_response/GetExpressCheckoutDetails/success.txt
================================================
TOKEN=EC%2d7HG122968U843864S&CHECKOUTSTATUS=PaymentActionNotInitiated&TIMESTAMP=2011%2d04%2d12T03%3a51%3a58Z&CORRELATIONID=6a0a92ea79ac3&ACK=Success&VERSION=69%2e0&BUILD=1824201&EMAIL=buyer_1297999636_per%40cerego%2ecom&PAYERID=9RWDTMRKKHQ8S&PAYERSTATUS=verified&FIRSTNAME=Test&LASTNAME=User&COUNTRYCODE=US&CURRENCYCODE=JPY&AMT=10&ITEMAMT=10&SHIPPINGAMT=0&HANDLINGAMT=0&TAXAMT=0&DESC=Instant%20Payment%20Request&NOTIFYURL=http%3a%2f%2fmerchant%2eexample%2ecom%2fnotify&INSURANCEAMT=0&SHIPDISCAMT=0&L_NAME0=Item1&L_QTY0=1&L_TAXAMT0=0&L_AMT0=10&L_DESC0=Item1%20Desc&L_ITEMWEIGHTVALUE0=%20%20%200%2e00000&L_ITEMLENGTHVALUE0=%20%20%200%2e00000&L_ITEMWIDTHVALUE0=%20%20%200%2e00000&L_ITEMHEIGHTVALUE0=%20%20%200%2e00000&L_ITEMCATEGORY0=Digital&PAYMENTREQUEST_0_CURRENCYCODE=JPY&PAYMENTREQUEST_0_AMT=10&PAYMENTREQUEST_0_ITEMAMT=10&PAYMENTREQUEST_0_SHIPPINGAMT=0&PAYMENTREQUEST_0_HANDLINGAMT=0&PAYMENTREQUEST_0_TAXAMT=0&PAYMENTREQUEST_0_DESC=Instant%20Payment%20Request&PAYMENTREQUEST_0_NOTIFYURL=http%3a%2f%2fmerchant%2eexample%2ecom%2fnotify&PAYMENTREQUEST_0_INSURANCEAMT=0&PAYMENTREQUEST_0_SHIPDISCAMT=0&PAYMENTREQUEST_0_INSURANCEOPTIONOFFERED=false&L_PAYMENTREQUEST_0_NAME0=Item1&L_PAYMENTREQUEST_0_QTY0=1&L_PAYMENTREQUEST_0_TAXAMT0=0&L_PAYMENTREQUEST_0_AMT0=10&L_PAYMENTREQUEST_0_DESC0=Item1%20Desc&L_PAYMENTREQUEST_0_ITEMWEIGHTVALUE0=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMLENGTHVALUE0=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWIDTHVALUE0=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMHEIGHTVALUE0=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMCATEGORY0=Digital&PAYMENTREQUESTINFO_0_ERRORCODE=0

================================================
FILE: spec/fake_response/GetExpressCheckoutDetails/with_billing_accepted_status.txt
================================================
TOKEN=EC%2d4D299105F9409730D&BILLINGAGREEMENTACCEPTEDSTATUS=1&CHECKOUTSTATUS=PaymentActionNotInitiated&TIMESTAMP=2011%2d08%2d08T01%3a56%3a13Z&CORRELATIONID=53a48e61dfca7&ACK=Success&VERSION=78%2e0&BUILD=2020243&EMAIL=client_1305381804_per%40matake%2ejp&PAYERID=CMJLWADKVFQYJ&PAYERSTATUS=verified&FIRSTNAME=Test&LASTNAME=User&COUNTRYCODE=US&CURRENCYCODE=JPY&AMT=0&SHIPPINGAMT=0&HANDLINGAMT=0&TAXAMT=0&INSURANCEAMT=0&SHIPDISCAMT=0&PAYMENTREQUEST_0_CURRENCYCODE=JPY&PAYMENTREQUEST_0_AMT=0&PAYMENTREQUEST_0_SHIPPINGAMT=0&PAYMENTREQUEST_0_HANDLINGAMT=0&PAYMENTREQUEST_0_TAXAMT=0&PAYMENTREQUEST_0_INSURANCEAMT=0&PAYMENTREQUEST_0_SHIPDISCAMT=0&PAYMENTREQUEST_0_INSURANCEOPTIONOFFERED=false&PAYMENTREQUESTINFO_0_ERRORCODE=0

================================================
FILE: spec/fake_response/GetRecurringPaymentsProfileDetails/failure.txt
================================================
PROFILEID=I%2dK1VFLRN3VVG0x&TIMESTAMP=2011%2d02%2d08T07%3a19%3a43Z&CORRELATIONID=edc954f1c34c5&ACK=Failure&VERSION=66%2e0&BUILD=1704252&L_ERRORCODE0=11552&L_SHORTMESSAGE0=Invalid%20profile%20ID&L_LONGMESSAGE0=The%20profile%20ID%20is%20invalid&L_SEVERITYCODE0=Error

================================================
FILE: spec/fake_response/GetRecurringPaymentsProfileDetails/success.txt
================================================
PROFILEID=I%2dK1VFLRN3VVG0&STATUS=Active&AUTOBILLOUTAMT=NoAutoBill&DESC=iKnow%21%20Subscription&MAXFAILEDPAYMENTS=0&SUBSCRIBERNAME=Test%20User&PROFILESTARTDATE=2011%2d02%2d03T15%3a00%3a00Z&NEXTBILLINGDATE=2011%2d03%2d04T10%3a00%3a00Z&NUMCYCLESCOMPLETED=1&NUMCYCLESREMAINING=18446744073709551615&OUTSTANDINGBALANCE=0&FAILEDPAYMENTCOUNT=0&LASTPAYMENTDATE=2011%2d02%2d04T10%3a50%3a56Z&LASTPAYMENTAMT=1000&TRIALAMTPAID=0&REGULARAMTPAID=1000&AGGREGATEAMT=1000&AGGREGATEOPTIONALAMT=0&FINALPAYMENTDUEDATE=1970%2d01%2d01T00%3a00%3a00Z&TIMESTAMP=2011%2d02%2d08T07%3a19%3a25Z&CORRELATIONID=8e18dd6545045&ACK=Success&VERSION=66%2e0&BUILD=1704252&SHIPTOSTREET=1%20Main%20St&SHIPTOCITY=San%20Jose&SHIPTOSTATE=CA&SHIPTOZIP=95131&SHIPTOCOUNTRYCODE=US&SHIPTOCOUNTRY=US&SHIPTOCOUNTRYNAME=United%20States&SHIPADDRESSOWNER=PayPal&SHIPADDRESSSTATUS=Unconfirmed&BILLINGPERIOD=Month&BILLINGFREQUENCY=1&TOTALBILLINGCYCLES=0&CURRENCYCODE=JPY&AMT=1000&SHIPPINGAMT=0&TAXAMT=0&REGULARBILLINGPERIOD=Month&REGULARBILLINGFREQUENCY=1&REGULARTOTALBILLINGCYCLES=0&REGULARCURRENCYCODE=JPY&REGULARAMT=1000&REGULARSHIPPINGAMT=0&REGULARTAXAMT=0

================================================
FILE: spec/fake_response/GetTransactionDetails/failure.txt
================================================
ADDRESSOWNER=PayPal&ADDRESSSTATUS=None&TIMESTAMP=2012%2d02%2d23T21%3a07%3a22Z&CORRELATIONID=4e18d75a23953&ACK=Failure&VERSION=78%2e0&BUILD=2571254&L_ERRORCODE0=10004&L_SHORTMESSAGE0=Transaction%20refused%20because%20of%20an%20invalid%20argument%2e%20See%20additional%20error%20messages%20for%20details%2e&L_LONGMESSAGE0=The%20transaction%20id%20is%20not%20valid&L_SEVERITYCODE0=Error&PENDINGREASON=None&REASONCODE=None

================================================
FILE: spec/fake_response/GetTransactionDetails/success.txt
================================================
RECEIVEREMAIL=test%40email%2ecom&RECEIVERID=ABCDEFG12345&EMAIL=test%40email%2ecom&PAYERID=12345ABCDEFG&PAYERSTATUS=verified&COUNTRYCODE=US&SHIPTONAME=Billy%20Bob&SHIPTOSTREET=1%20Main%20St&SHIPTOCITY=San%20Jose&SHIPTOSTATE=CA&SHIPTOCOUNTRYCODE=US&SHIPTOCOUNTRYNAME=United%20States&SHIPTOZIP=95131&ADDRESSOWNER=PayPal&ADDRESSSTATUS=Confirmed&SALESTAX=0%2e00&SUBJECT=Test%20Subject&TIMESTAMP=2012%2d02%2d23T20%3a51%3a29Z&CORRELATIONID=4fc0a06cbaec7&ACK=Success&VERSION=78%2e0&BUILD=2571254&FIRSTNAME=Billy&LASTNAME=Bob&TRANSACTIONID=123456789ABCD&TRANSACTIONTYPE=cart&PAYMENTTYPE=instant&ORDERTIME=2012%2d02%2d13T23%3a09%3a46Z&AMT=740%2e43&FEEAMT=21%2e77&TAXAMT=0%2e00&SHIPPINGAMT=0%2e00&HANDLINGAMT=0%2e00&CURRENCYCODE=USD&PAYMENTSTATUS=Completed&PENDINGREASON=None&REASONCODE=None&PROTECTIONELIGIBILITY=Eligible&PROTECTIONELIGIBILITYTYPE=ItemNotReceivedEligible%2cUnauthorizedPaymentEligible&L_NAME0=Item%201&L_NAME1=Item%202&L_QTY0=1&L_QTY1=1&L_SHIPPINGAMT0=0%2e00&L_SHIPPINGAMT1=0%2e00&L_HANDLINGAMT0=0%2e00&L_HANDLINGAMT1=0%2e00&L_CURRENCYCODE0=USD&L_CURRENCYCODE1=USD&L_AMT0=687%2e93&L_AMT1=52%2e50

================================================
FILE: spec/fake_response/IPN/invalid.txt
================================================
INVALID

================================================
FILE: spec/fake_response/IPN/valid.txt
================================================
VERIFIED

================================================
FILE: spec/fake_response/ManageRecurringPaymentsProfileStatus/failure.txt
================================================
TIMESTAMP=2011%2d02%2d08T07%3a21%3a08Z&CORRELATIONID=aa160cad77481&ACK=Failure&VERSION=66%2e0&BUILD=1704252&L_ERRORCODE0=11556&L_SHORTMESSAGE0=Invalid%20profile%20status%20for%20cancel%20action%3b%20profile%20should%20be%20active%20or%20suspended&L_LONGMESSAGE0=Invalid%20profile%20status%20for%20cancel%20action%3b%20profile%20should%20be%20active%20or%20suspended&L_SEVERITYCODE0=Error

================================================
FILE: spec/fake_response/ManageRecurringPaymentsProfileStatus/success.txt
================================================
PROFILEID=I%2dK1VFLRN3VVG0&TIMESTAMP=2011%2d02%2d08T07%3a20%3a44Z&CORRELATIONID=86bde487ccba6&ACK=Success&VERSION=66%2e0&BUILD=1704252

================================================
FILE: spec/fake_response/RefundTransaction/full.txt
================================================
REFUNDTRANSACTIONID=6D456341FS516215S&FEEREFUNDAMT=0%2e50&GROSSREFUNDAMT=10%2e00&NETREFUNDAMT=9%2e50&CURRENCYCODE=USD&TOTALREFUNDEDAMOUNT=10%2e00&TIMESTAMP=2011%2d05%2d21T14%3a13%3a32Z&CORRELATIONID=862bfc523cca2&ACK=Success&VERSION=72%2e0&BUILD=1882144

================================================
FILE: spec/fake_response/SetExpressCheckout/failure.txt
================================================
TIMESTAMP=2011%2d02%2d02T02%3a16%3a50Z&CORRELATIONID=379d1b7f97afb&ACK=Failure&L_ERRORCODE0=10001&L_SHORTMESSAGE0=Internal%20Error&L_LONGMESSAGE0=Timeout%20processing%20request

================================================
FILE: spec/fake_response/SetExpressCheckout/success.txt
================================================
ACK=Success&BUILD=1721431&CORRELATIONID=5549ea3a78af1&TIMESTAMP=2011-02-02T02%3A02%3A18Z&TOKEN=EC-5YJ90598G69065317&VERSION=66.0

================================================
FILE: spec/helpers/fake_response_helper.rb
================================================
require 'fakeweb'

module FakeResponseHelper

  def fake_response(file_path, api = :NVP, options = {})
    endpoint = case api
    when :NVP
      Paypal::NVP::Request.endpoint
    when :IPN
      Paypal::IPN.endpoint
    else
      raise "Non-supported API: #{api}"
    end
    FakeWeb.register_uri(
      :post,
      endpoint,
      options.merge(
        :body => File.read(File.join(File.dirname(__FILE__), '../fake_response', "#{file_path}.txt"))
      )
    )
  end

  def request_to(endpoint, method = :get)
    raise_error(
      FakeWeb::NetConnectNotAllowedError,
      "Real HTTP connections are disabled. Unregistered request: #{method.to_s.upcase} #{endpoint}"
    )
  end

end

FakeWeb.allow_net_connect = false
include FakeResponseHelper

================================================
FILE: spec/paypal/exception/api_error_spec.rb
================================================
require 'spec_helper.rb'

describe Paypal::Exception::APIError do
  let(:error) { Paypal::Exception::APIError.new(params) }

  context 'when Hash is given' do
    let :params do
      {
        :VERSION=>"66.0",
        :TIMESTAMP=>"2011-03-03T06:33:51Z",
        :CORRELATIONID=>"758ebdc546b9c",
        :BUILD=>"1741654",
        :ACK=>"Failure",
        :L_SEVERITYCODE0=>"Error",
        :L_ERRORCODE0=>"10411",
        :L_LONGMESSAGE0=>"This Express Checkout session has expired.  Token value is no longer valid.",
        :L_SHORTMESSAGE0=>"This Express Checkout session has expired.",
        :L_SEVERITYCODE1=>"Error",
        :L_ERRORCODE1=>"2468",
        :L_LONGMESSAGE1=>"Sample of a long message for the second item.",
        :L_SHORTMESSAGE1=>"Second short message.",
      }
    end

    describe "#message" do
      it "aggregates short messages" do
        error.message.should ==
          "PayPal API Error: 'This Express Checkout session has expired.', 'Second short message.'"
      end
    end

    describe '#subject' do
      subject { error.response }
      its(:raw) { should == params }
      Paypal::Exception::APIError::Response.attribute_mapping.each do |key, attribute|
        its(attribute) { should == params[key] }
      end

      describe '#details' do
        subject { error.response.details.first }
        Paypal::Exception::APIError::Response::Detail.attribute_mapping.each do |key, attribute|
          its(attribute) { should == params[:"L_#{key}0"] }
        end
      end
    end
  end

  context 'when unknown params given' do
    let :params do
      {
        :UNKNOWN => 'Unknown',
        :L_UNKNOWN0 => 'Unknown Detail'
      }
    end

    it 'should warn' do
      Paypal.logger.should_receive(:warn).with(
        "Ignored Parameter (Paypal::Exception::APIError::Response): UNKNOWN=Unknown"
      )
      Paypal.logger.should_receive(:warn).with(
        "Ignored Parameter (Paypal::Exception::APIError::Response::Detail): UNKNOWN=Unknown Detail"
      )
      error
    end
    describe '#response' do
      subject { error.response }
      its(:raw) { should == params }
    end
    its(:message) { should == "PayPal API Error" }
  end

  context 'otherwise' do
    subject { error }
    let(:params) { 'Failure' }
    its(:response) { should == params }
    its(:message) { should == "PayPal API Error" }
  end
end

================================================
FILE: spec/paypal/exception/http_error_spec.rb
================================================
require 'spec_helper.rb'

describe Paypal::Exception::HttpError do
  subject { Paypal::Exception::HttpError.new(400, 'BadRequest', 'You are bad man!') }
  its(:code)    { should == 400 }
  its(:message) { should == 'BadRequest' }
  its(:body)    { should == 'You are bad man!' }
end

================================================
FILE: spec/paypal/express/request_spec.rb
================================================
require 'spec_helper.rb'

describe Paypal::Express::Request do
  class Paypal::Express::Request
    attr_accessor :_sent_params_, :_method_
    def post_with_logging(method, params)
      self._method_ = method
      self._sent_params_ = params
      post_without_logging method, params
    end
    alias_method_chain :post, :logging
  end

  let(:return_url) { 'http://example.com/success' }
  let(:cancel_url) { 'http://example.com/cancel' }
  let(:nvp_endpoint) { Paypal::NVP::Request::ENDPOINT[:production] }
  let :attributes do
    {
      :username => 'nov',
      :password => 'password',
      :signature => 'sig'
    }
  end

  let :instance do
    Paypal::Express::Request.new attributes
  end

  let :instant_payment_request do
    Paypal::Payment::Request.new(
      :amount => 1000,
      :description => 'Instant Payment Request'
    )
  end

  let :many_items do
    items = Array.new
    (1..20).each do |index|
      items << Paypal::Payment::Request::Item.new(
        :name => "Item#{index.to_s}",
        :description => "A new Item #{index.to_s}",
        :amount => 50.00,
        :quantity => 1
      )
    end
  end

  let :instant_payment_request_with_many_items do
       Paypal::Payment::Request.new(
      :amount => 1000,
      :description => 'Instant Payment Request',
      :items => many_items
    )
  end

  let :recurring_payment_request do
    Paypal::Payment::Request.new(
      :billing_type => :RecurringPayments,
      :billing_agreement_description => 'Recurring Payment Request'
    )
  end

  let :recurring_profile do
    Paypal::Payment::Recurring.new(
      :start_date => Time.utc(2011, 2, 8, 9, 0, 0),
      :description => 'Recurring Profile',
      :billing => {
        :period => :Month,
        :frequency => 1,
        :amount => 1000
      }
    )
  end

  let :reference_transaction_request do
    Paypal::Payment::Request.new(
      :billing_type => :MerchantInitiatedBilling,
      :billing_agreement_description => 'Billing Agreement Request'
    )
  end

  describe '.new' do
    context 'when any required parameters are missing' do
      it 'should raise AttrRequired::AttrMissing' do
        attributes.keys.each do |missing_key|
          insufficient_attributes = attributes.reject do |key, value|
            key == missing_key
          end
          expect do
            Paypal::Express::Request.new insufficient_attributes
          end.to raise_error AttrRequired::AttrMissing
        end
      end
    end

    context 'when all required parameters are given' do
      it 'should succeed' do
        expect do
          Paypal::Express::Request.new attributes
        end.not_to raise_error AttrRequired::AttrMissing
      end
    end
  end

  describe '#setup' do
    it 'should return Paypal::Express::Response' do
      fake_response 'SetExpressCheckout/success'
      response = instance.setup recurring_payment_request, return_url, cancel_url
      response.should be_instance_of Paypal::Express::Response
    end

    it 'should support no_shipping option' do
      expect do
        instance.setup instant_payment_request, return_url, cancel_url, :no_shipping => true
      end.to request_to nvp_endpoint, :post
      instance._method_.should == :SetExpressCheckout
      instance._sent_params_.should == {
        :PAYMENTREQUEST_0_DESC => 'Instant Payment Request',
        :RETURNURL => return_url,
        :CANCELURL => cancel_url,
        :PAYMENTREQUEST_0_AMT => '1000.00',
        :PAYMENTREQUEST_0_TAXAMT => "0.00",
        :PAYMENTREQUEST_0_SHIPPINGAMT => "0.00",
        :REQCONFIRMSHIPPING => 0,
        :NOSHIPPING => 1
      }
    end

    it 'should support allow_note=false option' do
      expect do
        instance.setup instant_payment_request, return_url, cancel_url, :allow_note => false
      end.to request_to nvp_endpoint, :post
      instance._method_.should == :SetExpressCheckout
      instance._sent_params_.should == {
        :PAYMENTREQUEST_0_DESC => 'Instant Payment Request',
        :RETURNURL => return_url,
        :CANCELURL => cancel_url,
        :PAYMENTREQUEST_0_AMT => '1000.00',
        :PAYMENTREQUEST_0_TAXAMT => "0.00",
        :PAYMENTREQUEST_0_SHIPPINGAMT => "0.00",
        :ALLOWNOTE => 0
      }
    end

    {
      :solution_type => :SOLUTIONTYPE,
      :landing_page => :LANDINGPAGE,
      :email => :EMAIL,
      :brand => :BRANDNAME,
      :locale => :LOCALECODE,
      :logo => :LOGOIMG,
      :cart_border_color => :CARTBORDERCOLOR,
      :payflow_color => :PAYFLOWCOLOR
    }.each do |option_key, param_key|
      it "should support #{option_key} option" do
        expect do
          instance.setup instant_payment_request, return_url, cancel_url, option_key => 'some value'
        end.to request_to nvp_endpoint, :post
        instance._method_.should == :SetExpressCheckout
        instance._sent_params_.should include param_key
        instance._sent_params_[param_key].should == 'some value'
      end
    end

    context 'when instance payment request given' do
      it 'should call SetExpressCheckout' do
        expect do
          instance.setup instant_payment_request, return_url, cancel_url
        end.to request_to nvp_endpoint, :post
        instance._method_.should == :SetExpressCheckout
        instance._sent_params_.should == {
          :PAYMENTREQUEST_0_DESC => 'Instant Payment Request',
          :RETURNURL => return_url,
          :CANCELURL => cancel_url,
          :PAYMENTREQUEST_0_AMT => '1000.00',
          :PAYMENTREQUEST_0_TAXAMT => "0.00",
          :PAYMENTREQUEST_0_SHIPPINGAMT => "0.00"
        }
      end
    end

    context 'when recurring payment request given' do
      it 'should call SetExpressCheckout' do
        expect do
          instance.setup recurring_payment_request, return_url, cancel_url
        end.to request_to nvp_endpoint, :post
        instance._method_.should == :SetExpressCheckout
        instance._sent_params_.should == {
          :L_BILLINGTYPE0 => :RecurringPayments,
          :L_BILLINGAGREEMENTDESCRIPTION0 => 'Recurring Payment Request',
          :RETURNURL => return_url,
          :CANCELURL => cancel_url,
          :PAYMENTREQUEST_0_AMT => '0.00',
          :PAYMENTREQUEST_0_TAXAMT => "0.00",
          :PAYMENTREQUEST_0_SHIPPINGAMT => "0.00"
        }
      end
    end

    context 'when reference transaction request given' do
      it 'should call SetExpressCheckout' do
        expect do
          instance.setup reference_transaction_request, return_url, cancel_url
        end.to request_to nvp_endpoint, :post
        instance._method_.should == :SetExpressCheckout
        instance._sent_params_.should == {
          :L_BILLINGTYPE0 => :MerchantInitiatedBilling,
          :L_BILLINGAGREEMENTDESCRIPTION0 => 'Billing Agreement Request',
          :RETURNURL => return_url,
          :CANCELURL => cancel_url,
          :PAYMENTREQUEST_0_AMT => '0.00',
          :PAYMENTREQUEST_0_TAXAMT => "0.00",
          :PAYMENTREQUEST_0_SHIPPINGAMT => "0.00"
        }
      end
    end
  end

  describe '#details' do
    it 'should return Paypal::Express::Response' do
      fake_response 'GetExpressCheckoutDetails/success'
      response = instance.details 'token'
      response.should be_instance_of Paypal::Express::Response
    end

    it 'should call GetExpressCheckoutDetails' do
      expect do
        instance.details 'token'
      end.to request_to nvp_endpoint, :post
      instance._method_.should == :GetExpressCheckoutDetails
      instance._sent_params_.should == {
        :TOKEN => 'token'
      }
    end
  end

  describe '#transaction_details' do
    it 'should return Paypal::Express::Response' do
      fake_response 'GetTransactionDetails/success'
      response = instance.transaction_details 'transaction_id'
      response.should be_instance_of Paypal::Express::Response
    end

    it 'should call GetTransactionDetails' do
      expect do
        instance.transaction_details 'transaction_id'
      end.to request_to nvp_endpoint, :post
      instance._method_.should == :GetTransactionDetails
      instance._sent_params_.should == {
        :TRANSACTIONID=> 'transaction_id'
      }
    end

    it 'should fail with bad transaction id' do
      expect do
        fake_response 'GetTransactionDetails/failure'
        response = instance.transaction_details 'bad_transaction_id'
      end.to raise_error(Paypal::Exception::APIError)
    end

    it 'should handle all attributes' do
      Paypal.logger.should_not_receive(:warn)
      fake_response 'GetTransactionDetails/success'
      response = instance.transaction_details 'transaction_id'
    end
  end

  describe "#capture!" do
    it 'should return Paypal::Express::Response' do
      fake_response 'DoCapture/success'
      response = instance.capture! 'authorization_id', 181.98, :BRL
      response.should be_instance_of Paypal::Express::Response
    end

    it 'should call DoExpressCheckoutPayment' do
      expect do
        instance.capture! 'authorization_id', 181.98, :BRL
      end.to request_to nvp_endpoint, :post

      instance._method_.should == :DoCapture
      instance._sent_params_.should == {
        :AUTHORIZATIONID => 'authorization_id',
        :COMPLETETYPE => 'Complete',
        :AMT => 181.98,
        :CURRENCYCODE => :BRL
      }
    end

    it 'should call DoExpressCheckoutPayment with NotComplete capture parameter' do
      expect do
        instance.capture! 'authorization_id', 181.98, :BRL, 'NotComplete'
      end.to request_to nvp_endpoint, :post

      instance._method_.should == :DoCapture
      instance._sent_params_.should == {
        :AUTHORIZATIONID => 'authorization_id',
        :COMPLETETYPE => 'NotComplete',
        :AMT => 181.98,
        :CURRENCYCODE => :BRL
      }
    end
  end

  describe "#void!" do
    it 'should return Paypal::Express::Response' do
      fake_response 'DoVoid/success'
      response = instance.void! 'authorization_id', note: "note"
      response.should be_instance_of Paypal::Express::Response
    end

    it 'should call DoVoid' do
      expect do
        instance.void! 'authorization_id', note: "note"
      end.to request_to nvp_endpoint, :post

      instance._method_.should == :DoVoid
      instance._sent_params_.should == {
        :AUTHORIZATIONID => 'authorization_id',
        :NOTE => "note"
      }
    end
  end

  describe '#checkout!' do
    it 'should return Paypal::Express::Response' do
      fake_response 'DoExpressCheckoutPayment/success'
      response = instance.checkout! 'token', 'payer_id', instant_payment_request
      response.should be_instance_of Paypal::Express::Response
    end

    it 'should call DoExpressCheckoutPayment' do
      expect do
        instance.checkout! 'token', 'payer_id', instant_payment_request
      end.to request_to nvp_endpoint, :post
      instance._method_.should == :DoExpressCheckoutPayment
      instance._sent_params_.should == {
        :PAYERID => 'payer_id',
        :TOKEN => 'token',
        :PAYMENTREQUEST_0_DESC => 'Instant Payment Request',
        :PAYMENTREQUEST_0_AMT => '1000.00',
        :PAYMENTREQUEST_0_TAXAMT => "0.00",
        :PAYMENTREQUEST_0_SHIPPINGAMT => "0.00"
      }
    end

    context "with many items" do
      before do
        fake_response 'DoExpressCheckoutPayment/success_with_many_items'
      end

      it 'should handle all attributes' do
        Paypal.logger.should_not_receive(:warn)
        response = instance.checkout! 'token', 'payer_id', instant_payment_request_with_many_items
      end

      it 'should return Paypal::Express::Response' do
        response = instance.checkout! 'token', 'payer_id', instant_payment_request_with_many_items
        response.should be_instance_of Paypal::Express::Response
      end

      it 'should return twenty items' do
        response = instance.checkout! 'token', 'payer_id', instant_payment_request_with_many_items
        instance._method_.should == :DoExpressCheckoutPayment
        response.items.count.should == 20
      end
    end
  end

  describe '#subscribe!' do
    it 'should return Paypal::Express::Response' do
      fake_response 'CreateRecurringPaymentsProfile/success'
      response = instance.subscribe! 'token', recurring_profile
      response.should be_instance_of Paypal::Express::Response
    end

    it 'should call CreateRecurringPaymentsProfile' do
      expect do
        instance.subscribe! 'token', recurring_profile
      end.to request_to nvp_endpoint, :post
      instance._method_.should == :CreateRecurringPaymentsProfile
      instance._sent_params_.should == {
        :DESC => 'Recurring Profile',
        :TOKEN => 'token',
        :SHIPPINGAMT => '0.00',
        :AMT => '1000.00',
        :BILLINGFREQUENCY => 1,
        :MAXFAILEDPAYMENTS => 0,
        :BILLINGPERIOD => :Month,
        :TAXAMT => '0.00',
        :PROFILESTARTDATE => '2011-02-08 09:00:00',
        :TOTALBILLINGCYCLES => 0
      }
    end
  end

  describe '#subscription' do
    it 'should return Paypal::Express::Response' do
      fake_response 'GetRecurringPaymentsProfileDetails/success'
      response = instance.subscription 'profile_id'
      response.should be_instance_of(Paypal::Express::Response)
    end

    it 'should call GetRecurringPaymentsProfileDetails' do
      expect do
        instance.subscription 'profile_id'
      end.to request_to nvp_endpoint, :post
      instance._method_.should == :GetRecurringPaymentsProfileDetails
      instance._sent_params_.should == {
        :PROFILEID => 'profile_id'
      }
    end
  end

  describe '#renew!' do
    it 'should return Paypal::Express::Response' do
      fake_response 'ManageRecurringPaymentsProfileStatus/success'
      response = instance.renew! 'profile_id', :Cancel
      response.should be_instance_of Paypal::Express::Response
    end

    it 'should call ManageRecurringPaymentsProfileStatus' do
      expect do
        instance.renew! 'profile_id', :Cancel
      end.to request_to nvp_endpoint, :post
      instance._method_.should == :ManageRecurringPaymentsProfileStatus
      instance._sent_params_.should == {
        :ACTION => :Cancel,
        :PROFILEID => 'profile_id'
      }
    end
  end

  describe '#cancel!' do
    it 'should return Paypal::Express::Response' do
      fake_response 'ManageRecurringPaymentsProfileStatus/success'
      response = instance.cancel! 'profile_id'
      response.should be_instance_of(Paypal::Express::Response)
    end

    it 'should call ManageRecurringPaymentsProfileStatus' do
      expect do
        instance.cancel! 'profile_id'
      end.to request_to nvp_endpoint, :post
      instance._method_.should == :ManageRecurringPaymentsProfileStatus
      instance._sent_params_.should == {
        :ACTION => :Cancel,
        :PROFILEID => 'profile_id'
      }
    end
  end

  describe '#suspend!' do
    it 'should return Paypal::Express::Response' do
      fake_response 'ManageRecurringPaymentsProfileStatus/success'
      response = instance.cancel! 'profile_id'
      response.should be_instance_of Paypal::Express::Response
    end

    it 'should call ManageRecurringPaymentsProfileStatus' do
      expect do
        instance.suspend! 'profile_id'
      end.to request_to nvp_endpoint, :post
      instance._method_.should == :ManageRecurringPaymentsProfileStatus
      instance._sent_params_.should == {
        :ACTION => :Suspend,
        :PROFILEID => 'profile_id'
      }
    end
  end

  describe '#reactivate!' do
    it 'should return Paypal::Express::Response' do
      fake_response 'ManageRecurringPaymentsProfileStatus/success'
      response = instance.cancel! 'profile_id'
      response.should be_instance_of Paypal::Express::Response
    end

    it 'should call ManageRecurringPaymentsProfileStatus' do
      expect do
        instance.reactivate! 'profile_id'
      end.to request_to nvp_endpoint, :post
      instance._method_.should == :ManageRecurringPaymentsProfileStatus
      instance._sent_params_.should == {
        :ACTION => :Reactivate,
        :PROFILEID => 'profile_id'
      }
    end
  end

  describe '#agree!' do
    it 'should return Paypal::Express::Response' do
      fake_response 'CreateBillingAgreement/success'
      response = instance.agree! 'token'
      response.should be_instance_of Paypal::Express::Response
    end

    it 'should call CreateBillingAgreement' do
      expect do
        instance.agree! 'token'
      end.to request_to nvp_endpoint, :post
      instance._method_.should == :CreateBillingAgreement
      instance._sent_params_.should == {
        :TOKEN => 'token'
      }
    end
  end

  describe '#agreement' do
    it 'should return Paypal::Express::Response' do
      fake_response 'BillAgreementUpdate/fetch'
      response = instance.agreement 'reference_id'
      response.should be_instance_of Paypal::Express::Response
    end

    it 'should call BillAgreementUpdate' do
      expect do
        instance.agreement 'reference_id'
      end.to request_to nvp_endpoint, :post
      instance._method_.should == :BillAgreementUpdate
      instance._sent_params_.should == {
        :REFERENCEID => 'reference_id'
      }
    end
  end

  describe '#charge!' do
    it 'should return Paypal::Express::Response' do
      fake_response 'DoReferenceTransaction/success'
      response = instance.charge! 'billing_agreement_id', 1000
      response.should be_instance_of Paypal::Express::Response
    end

    it 'should call DoReferenceTransaction' do
      expect do
        instance.charge! 'billing_agreement_id', 1000, :currency_code => :JPY
      end.to request_to nvp_endpoint, :post
      instance._method_.should == :DoReferenceTransaction
      instance._sent_params_.should == {
        :REFERENCEID => 'billing_agreement_id',
        :AMT => '1000.00',
        :PAYMENTACTION => :Sale,
        :CURRENCYCODE => :JPY
      }
    end
  end

  describe '#revoke!' do
    it 'should return Paypal::Express::Response' do
      fake_response 'BillAgreementUpdate/revoke'
      response = instance.revoke! 'reference_id'
      response.should be_instance_of Paypal::Express::Response
    end

    it 'should call BillAgreementUpdate' do
      expect do
        instance.revoke! 'reference_id'
      end.to request_to nvp_endpoint, :post
      instance._method_.should == :BillAgreementUpdate
      instance._sent_params_.should == {
        :REFERENCEID => 'reference_id',
        :BillingAgreementStatus => :Canceled
      }
    end
  end

  describe '#refund!' do
    it 'should return Paypal::Express::Response' do
      fake_response 'RefundTransaction/full'
      response = instance.refund! 'transaction_id'
      response.should be_instance_of Paypal::Express::Response
    end

    it 'should call RefundTransaction' do
      expect do
        instance.refund! 'transaction_id'
      end.to request_to nvp_endpoint, :post
      instance._method_.should == :RefundTransaction
      instance._sent_params_.should == {
        :TRANSACTIONID => 'transaction_id',
        :REFUNDTYPE => :Full
      }
    end
  end
end


================================================
FILE: spec/paypal/express/response_spec.rb
================================================
require 'spec_helper.rb'

describe Paypal::Express::Response do
  before { fake_response 'SetExpressCheckout/success' }

  let(:return_url) { 'http://example.com/success' }
  let(:cancel_url) { 'http://example.com/cancel' }
  let :request do
    Paypal::Express::Request.new(
      :username => 'nov',
      :password => 'password',
      :signature => 'sig'
    )
  end
  let :payment_request do
    Paypal::Payment::Request.new( 
      :billing_type => :RecurringPayments,
      :billing_agreement_description => 'Recurring Payment Request'
    )
  end
  let(:response) { request.setup payment_request, return_url, cancel_url }

  describe '#redirect_uri' do
    subject { response.redirect_uri }
    it { should include 'https://www.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=' }
  end

  describe '#popup_uri' do
    subject { response.popup_uri }
    it { should include 'https://www.paypal.com/incontext?token=' }
  end

  context 'when pay_on_paypal option is given' do
    let(:response) { request.setup payment_request, return_url, cancel_url, :pay_on_paypal => true }

    subject { response }
    its(:pay_on_paypal) { should be_true }
    its(:query) { should include(:useraction => 'commit') }

    describe '#redirect_uri' do
      subject { response.redirect_uri }
      it { should include 'useraction=commit' }
    end

    describe '#popup_uri' do
      subject { response.popup_uri }
      it { should include 'useraction=commit' }
    end
  end

  context 'when sandbox mode' do
    before do
      Paypal.sandbox!
      fake_response 'SetExpressCheckout/success'
    end
    after { Paypal.sandbox = false }

    describe '#redirect_uri' do
      subject { response.redirect_uri }
      it { should include 'https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=' }
    end

    describe '#popup_uri' do
      subject { response.popup_uri }
      it { should include 'https://www.sandbox.paypal.com/incontext?token=' }
    end
  end

  context 'when mobile option is given' do
    let(:response) { request.setup payment_request, return_url, cancel_url, :mobile => true }

    subject { response }

    describe '#redirect_uri' do
      subject { response.redirect_uri }
      it { should include 'https://www.paypal.com/cgi-bin/webscr?cmd=_express-checkout-mobile&token=' }
    end
  end

end

================================================
FILE: spec/paypal/ipn_spec.rb
================================================
require 'spec_helper'

describe Paypal::IPN do
  describe '.verify!' do
    context 'when valid' do
      before { fake_response 'IPN/valid', :IPN }
      subject { Paypal::IPN.verify!('raw-post-body') }
      it { should be_true }
    end

    context 'when invalid' do
      before { fake_response 'IPN/invalid', :IPN }
      subject {}
      it do
        expect { Paypal::IPN.verify!('raw-post-body') }.to raise_error(Paypal::Exception::APIError)
      end
    end
  end
end

================================================
FILE: spec/paypal/nvp/request_spec.rb
================================================
require 'spec_helper.rb'

describe Paypal::NVP::Request do
  let :attributes do
    {
      :username => 'nov',
      :password => 'password',
      :signature => 'sig'
    }
  end

  let :instance do
    Paypal::NVP::Request.new attributes
  end

  describe '.new' do
    context 'when any required parameters are missing' do
      it 'should raise AttrRequired::AttrMissing' do
        attributes.keys.each do |missing_key|
          insufficient_attributes = attributes.reject do |key, value|
            key == missing_key
          end
          expect do
            Paypal::NVP::Request.new insufficient_attributes
          end.to raise_error AttrRequired::AttrMissing
        end
      end
    end

    context 'when all required parameters are given' do
      it 'should succeed' do
        expect do
          Paypal::NVP::Request.new attributes
        end.not_to raise_error AttrRequired::AttrMissing
      end

      it 'should setup endpoint and version' do
        client = Paypal::NVP::Request.new attributes
        client.class.endpoint.should == Paypal::NVP::Request::ENDPOINT[:production]
      end

      it 'should support sandbox mode' do
        sandbox_mode do
          client = Paypal::NVP::Request.new attributes
          client.class.endpoint.should == Paypal::NVP::Request::ENDPOINT[:sandbox]
        end
      end
    end

    context 'when optional parameters are given' do
      let(:optional_attributes) do
        { :subject => 'user@example.com' }
      end

      it 'should setup subject' do
        client = Paypal::NVP::Request.new attributes.merge(optional_attributes)
        client.subject.should == 'user@example.com'
      end
    end
  end

  describe '#common_params' do
    {
      :username => :USER,
      :password => :PWD,
      :signature => :SIGNATURE,
      :subject => :SUBJECT,
      :version => :VERSION
    }.each do |option_key, param_key|
      it "should include :#{param_key}" do
        instance.common_params.should include(param_key)
      end

      it "should set :#{param_key} as #{option_key}" do
        instance.common_params[param_key].should == instance.send(option_key)
      end
    end
  end

  describe '#request' do
    it 'should POST to NPV endpoint' do
      expect do
        instance.request :RPCMethod
      end.to request_to Paypal::NVP::Request::ENDPOINT[:production], :post
    end

    context 'when got API error response' do
      before do
        fake_response 'SetExpressCheckout/failure'
      end

      it 'should raise Paypal::Exception::APIError' do
        expect do
          instance.request :SetExpressCheckout
        end.to raise_error(Paypal::Exception::APIError)
      end
    end

    context 'when got HTTP error response' do
      before do
        FakeWeb.register_uri(
          :post,
          Paypal::NVP::Request::ENDPOINT[:production],
          :body => "Invalid Request",
          :status => ["400", "Bad Request"]
        )
      end

      it 'should raise Paypal::Exception::HttpError' do
        expect do
          instance.request :SetExpressCheckout
        end.to raise_error(Paypal::Exception::HttpError)
      end
    end
  end
end

================================================
FILE: spec/paypal/nvp/response_spec.rb
================================================
require 'spec_helper.rb'

describe Paypal::NVP::Response do
  let(:return_url) { 'http://example.com/success' }
  let(:cancel_url) { 'http://example.com/cancel' }
  let :request do
    Paypal::Express::Request.new(
      :username => 'nov',
      :password => 'password',
      :signature => 'sig'
    )
  end

  let :payment_request do
    Paypal::Payment::Request.new(
      :amount => 1000,
      :description => 'Instant Payment Request'
    )
  end

  let :recurring_profile do
    Paypal::Payment::Recurring.new(
      :start_date => Time.utc(2011, 2, 8, 9, 0, 0),
      :description => 'Recurring Profile',
      :billing => {
        :period => :Month,
        :frequency => 1,
        :amount => 1000
      }
    )
  end

  describe '.new' do
    context 'when non-supported attributes are given' do
      it 'should ignore them and warn' do
        Paypal.logger.should_receive(:warn).with(
          "Ignored Parameter (Paypal::NVP::Response): ignored=Ignore me!"
        )
        Paypal::NVP::Response.new(
          :ignored => 'Ignore me!'
        )
      end
    end

    context 'when SetExpressCheckout response given' do
      before do
        fake_response 'SetExpressCheckout/success'
      end

      it 'should handle all attributes' do
        Paypal.logger.should_not_receive(:warn)
        response = request.setup payment_request, return_url, cancel_url
        response.token.should == 'EC-5YJ90598G69065317'
      end
    end

    context 'when GetExpressCheckoutDetails response given' do
      before do
        fake_response 'GetExpressCheckoutDetails/success'
      end

      it 'should handle all attributes' do
        Paypal.logger.should_not_receive(:warn)
        response = request.details 'token'
        response.payer.identifier.should == '9RWDTMRKKHQ8S'
        response.payment_responses.size.should == 1
        response.payment_info.size.should == 0
        response.payment_responses.first.should be_instance_of(Paypal::Payment::Response)
      end

      context 'when BILLINGAGREEMENTACCEPTEDSTATUS included' do
        before do
          fake_response 'GetExpressCheckoutDetails/with_billing_accepted_status'
        end

        it 'should handle all attributes' do
          Paypal.logger.should_not_receive(:warn)
          response = request.details 'token'
        end
      end
    end

    context 'when DoExpressCheckoutPayment response given' do
      before do
        fake_response 'DoExpressCheckoutPayment/success'
      end

      it 'should handle all attributes' do
        Paypal.logger.should_not_receive(:warn)
        response = request.checkout! 'token', 'payer_id', payment_request
        response.payment_responses.size.should == 0
        response.payment_info.size.should == 1
        response.payment_info.first.should be_instance_of(Paypal::Payment::Response::Info)
      end

      context 'when billing_agreement is included' do
        before do
          fake_response 'DoExpressCheckoutPayment/success_with_billing_agreement'
        end

        it 'should have billing_agreement' do
          Paypal.logger.should_not_receive(:warn)
          response = request.checkout! 'token', 'payer_id', payment_request
          response.billing_agreement.identifier.should == 'B-1XR87946TC504770W'
        end
      end
    end

    context 'when CreateRecurringPaymentsProfile response given' do
      before do
        fake_response 'CreateRecurringPaymentsProfile/success'
      end

      it 'should handle all attributes' do
        Paypal.logger.should_not_receive(:warn)
        response = request.subscribe! 'token', recurring_profile
        response.recurring.identifier.should == 'I-L8N58XFUCET3'
      end
    end

    context 'when GetRecurringPaymentsProfileDetails response given' do
      before do
        fake_response 'GetRecurringPaymentsProfileDetails/success'
      end

      it 'should handle all attributes' do
        Paypal.logger.should_not_receive(:warn)
        response = request.subscription 'profile_id'
        response.recurring.billing.amount.total.should == 1000
        response.recurring.regular_billing.paid.should == 1000
        response.recurring.summary.next_billing_date.should == '2011-03-04T10:00:00Z'
      end
    end

    context 'when ManageRecurringPaymentsProfileStatus response given' do
      before do
        fake_response 'ManageRecurringPaymentsProfileStatus/success'
      end

      it 'should handle all attributes' do
        Paypal.logger.should_not_receive(:warn)
        request.renew! 'profile_id', :Cancel
      end
    end
  end
end

================================================
FILE: spec/paypal/payment/common/amount_spec.rb
================================================
require 'spec_helper.rb'

describe Paypal::Payment::Common::Amount do
  let :keys do
    Paypal::Payment::Common::Amount.optional_attributes
  end

  describe '.new' do
    it 'should not allow nil for attributes' do
      amount = Paypal::Payment::Common::Amount.new
      keys.each do |key|
        amount.send(key).should == 0
      end
    end

    it 'should treat all attributes as Numeric' do
      # Integer
      attributes = keys.inject({}) do |attributes, key|
        attributes.merge!(key => "100")
      end
      amount = Paypal::Payment::Common::Amount.new attributes
      keys.each do |key|
        amount.send(key).should == 100
      end

      # Float
      attributes = keys.inject({}) do |attributes, key|
        attributes.merge!(key => "10.25")
      end
      amount = Paypal::Payment::Common::Amount.new attributes
      keys.each do |key|
        amount.send(key).should == 10.25
      end
    end
  end
end

================================================
FILE: spec/paypal/payment/recurring/activation_spec.rb
================================================
require 'spec_helper'

describe Paypal::Payment::Recurring::Activation do
  let :instance do
    Paypal::Payment::Recurring::Activation.new(
      :initial_amount => 100,
      :failed_action => 'ContinueOnFailure'
    )
  end

  describe '#to_params' do
    it 'should handle Recurring Profile activation parameters' do
      instance.to_params.should == {
        :INITAMT => '100.00',
        :FAILEDINITAMTACTION => 'ContinueOnFailure'
      }
    end
  end
end

================================================
FILE: spec/paypal/payment/recurring_spec.rb
================================================
require 'spec_helper.rb'

describe Paypal::Payment::Recurring do
  let :keys do
    Paypal::Payment::Recurring.optional_attributes
  end

  let :trial_attributes do
    {}
  end

  let :attributes do
    {
      :identifier => '12345',
      :description => 'Subscription Payment Profile',
      :status => 'Active',
      :start_date => '2011-02-03T15:00:00Z',
      :name => 'Nov Matake',
      :auto_bill => 'NoAutoBill',
      :max_fails => '0',
      :aggregate_amount => '1000',
      :aggregate_optional_amount => '0',
      :final_payment_date => '1970-01-01T00:00:00Z',
      :billing => {
        :amount => Paypal::Payment::Common::Amount.new(
          :total => '1000',
          :shipping => '0',
          :tax => '0'
        ),
        :currency_code => 'JPY',
        :period => 'Month',
        :frequency => '1',
        :total_cycles => '0',
        :trial => trial_attributes
      },
      :regular_billing => {
        :amount => '1000',
        :shipping_amount => '0',
        :tax_amount => '0',
        :currency_code => 'JPY',
        :period => 'Month',
        :frequency => '1',
        :total_cycles => '0',
        :paid => '1000'
      },
      :summary => {
        :next_billing_date => '2011-03-04T10:00:00Z',
        :cycles_completed => '1',
        :cycles_remaining => '18446744073709551615',
        :outstanding_balance => '0',
        :failed_count => '0',
        :last_payment_date => '2011-02-04T10:50:56Z',
        :last_payment_amount => '1000'
      }
    }
  end

  let :instance do
    Paypal::Payment::Recurring.new attributes
  end

  describe '.new' do
    it 'should accept all supported attributes' do
      instance.identifier.should == '12345'
      instance.description.should == 'Subscription Payment Profile'
      instance.status.should == 'Active'
      instance.start_date.should == '2011-02-03T15:00:00Z'
      instance.billing.trial.should be_nil
    end

    context 'when optional trial info given' do
      let :trial_attributes do
        {
          :period => 'Month',
          :frequency => '1',
          :total_cycles => '0',
          :currency_code => 'JPY',
          :amount => '1000',
          :shipping_amount => '0',
          :tax_amount => '0'
        }
      end
      it 'should setup trial billing info' do
        instance.billing.trial.should == Paypal::Payment::Recurring::Billing.new(trial_attributes)
      end
    end
  end

  describe '#to_params' do
    it 'should handle Recurring Profile parameters' do
      instance.to_params.should == {
        :AUTOBILLOUTAMT => 'NoAutoBill',
        :BILLINGFREQUENCY => 1,
        :SHIPPINGAMT => '0.00',
        :DESC => 'Subscription Payment Profile',
        :SUBSCRIBERNAME => 'Nov Matake',
        :BILLINGPERIOD => 'Month',
        :AMT => '1000.00',
        :MAXFAILEDPAYMENTS => 0,
        :TOTALBILLINGCYCLES => 0,
        :TAXAMT => '0.00',
        :PROFILESTARTDATE => '2011-02-03T15:00:00Z',
        :CURRENCYCODE => 'JPY'
      }
    end

    context 'when start_date is Time' do
      it 'should be stringfy' do
        instance = Paypal::Payment::Recurring.new attributes.merge(
          :start_date => Time.utc(2011, 2, 8, 15, 0, 0)
        )
        instance.start_date.should be_instance_of(Time)
        instance.to_params[:PROFILESTARTDATE].should == '2011-02-08 15:00:00'
      end
    end

    context 'when optional trial setting given' do
      let :trial_attributes do
        {
          :period => 'Month',
          :frequency => '1',
          :total_cycles => '0',
          :currency_code => 'JPY',
          :amount => '1000',
          :shipping_amount => '0',
          :tax_amount => '0'
        }
      end
      it 'should setup trial billing info' do
        instance.to_params.should == {
          :TRIALBILLINGPERIOD => "Month",
          :TRIALBILLINGFREQUENCY => 1,
          :TRIALTOTALBILLINGCYCLES => 0,
          :TRIALAMT => "1000.00",
          :TRIALCURRENCYCODE => "JPY",
          :TRIALSHIPPINGAMT => "0.00",
          :TRIALTAXAMT => "0.00",
          :BILLINGPERIOD => "Month",
          :BILLINGFREQUENCY => 1,
          :TOTALBILLINGCYCLES => 0,
          :AMT => "1000.00",
          :CURRENCYCODE => "JPY",
          :SHIPPINGAMT => "0.00",
          :TAXAMT => "0.00",
          :DESC => "Subscription Payment Profile",
          :MAXFAILEDPAYMENTS => 0,
          :AUTOBILLOUTAMT => "NoAutoBill",
          :PROFILESTARTDATE => "2011-02-03T15:00:00Z",
          :SUBSCRIBERNAME => "Nov Matake"
        }
      end
    end
  end

  describe '#numeric_attribute?' do
    let :numeric_attributes do
      [:aggregate_amount, :aggregate_optional_amount, :max_fails, :failed_count]
    end

    it 'should detect numeric attributes' do
      numeric_attributes.each do |key|
        instance.numeric_attribute?(key).should be_true
      end
      non_numeric_keys = keys - numeric_attributes
      non_numeric_keys.each do |key|
        instance.numeric_attribute?(key).should be_false
      end
    end
  end
end

================================================
FILE: spec/paypal/payment/request/item_spec.rb
================================================
require 'spec_helper.rb'

describe Paypal::Payment::Request::Item do
  let :instance do
    Paypal::Payment::Request::Item.new(
      :name => 'Name',
      :description => 'Description',
      :amount => 10,
      :quantity => 5,
      :category => :Digital,
      :number => '1'
    )
  end

  describe '#to_params' do
    it 'should handle Recurring Profile activation parameters' do
      instance.to_params(1).should == {
        :L_PAYMENTREQUEST_1_NAME0 => 'Name',
        :L_PAYMENTREQUEST_1_DESC0 => 'Description',
        :L_PAYMENTREQUEST_1_AMT0 => '10.00',
        :L_PAYMENTREQUEST_1_QTY0 => 5,
        :L_PAYMENTREQUEST_1_ITEMCATEGORY0 => :Digital,
        :L_PAYMENTREQUEST_1_NUMBER0 => '1'
      }
    end
  end
end

================================================
FILE: spec/paypal/payment/request_spec.rb
================================================
require 'spec_helper.rb'

describe Paypal::Payment::Request do
  let :instant_request do
    Paypal::Payment::Request.new(
      :amount => 25.7,
      :tax_amount => 0.4,
      :shipping_amount => 1.5,
      :currency_code => :JPY,
      :description => 'Instant Payment Request',
      :notify_url => 'http://merchant.example.com/notify',
      :invoice_number => 'ABC123',
      :custom => 'Custom',
      :items => [{
        :quantity => 2,
        :name => 'Item1',
        :description => 'Awesome Item 1!',
        :amount => 10.25
      }, {
        :quantity => 3,
        :name => 'Item2',
        :description => 'Awesome Item 2!',
        :amount => 1.1
      }],
      :custom_fields => {
        "l_surveychoice{n}" => 'abcd' # The '{n}' will be replaced with the index
      }
    )
  end

  let :recurring_request do
    Paypal::Payment::Request.new(
      :currency_code => :JPY,
      :billing_type => :RecurringPayments,
      :billing_agreement_description => 'Recurring Payment Request'
    )
  end

  let :reference_transaction_request do
    Paypal::Payment::Request.new(
      :currency_code => :JPY,
      :billing_type => :MerchantInitiatedBillingSingleAgreement,
      :billing_agreement_description => 'Reference Transaction Request'
    )
  end

  describe '.new' do
    it 'should handle Instant Payment parameters' do
      instant_request.amount.total.should == 25.7
      instant_request.amount.tax.should == 0.4
      instant_request.amount.shipping.should == 1.5
      instant_request.currency_code.should == :JPY
      instant_request.description.should == 'Instant Payment Request'
      instant_request.notify_url.should == 'http://merchant.example.com/notify'
    end

    it 'should handle Recurring Payment parameters' do
      recurring_request.currency_code.should == :JPY
      recurring_request.billing_type.should == :RecurringPayments
      recurring_request.billing_agreement_description.should == 'Recurring Payment Request'
    end

    it 'should handle Recurring Payment parameters' do
      reference_transaction_request.currency_code.should == :JPY
      reference_transaction_request.billing_type.should == :MerchantInitiatedBillingSingleAgreement
      reference_transaction_request.billing_agreement_description.should == 'Reference Transaction Request'
    end
  end

  describe '#to_params' do
    it 'should handle Instant Payment parameters' do
      instant_request.to_params.should == {
        :PAYMENTREQUEST_0_AMT => "25.70",
        :PAYMENTREQUEST_0_TAXAMT => "0.40",
        :PAYMENTREQUEST_0_SHIPPINGAMT => "1.50",
        :PAYMENTREQUEST_0_CURRENCYCODE => :JPY,
        :PAYMENTREQUEST_0_DESC => "Instant Payment Request",
        :PAYMENTREQUEST_0_NOTIFYURL => "http://merchant.example.com/notify",
        :PAYMENTREQUEST_0_ITEMAMT => "23.80",
        :PAYMENTREQUEST_0_INVNUM => "ABC123",
        :PAYMENTREQUEST_0_CUSTOM => "Custom",
        :L_PAYMENTREQUEST_0_NAME0 => "Item1",
        :L_PAYMENTREQUEST_0_DESC0 => "Awesome Item 1!",
        :L_PAYMENTREQUEST_0_AMT0 => "10.25",
        :L_PAYMENTREQUEST_0_QTY0 => 2,
        :L_PAYMENTREQUEST_0_NAME1 => "Item2",
        :L_PAYMENTREQUEST_0_DESC1 => "Awesome Item 2!",
        :L_PAYMENTREQUEST_0_AMT1 => "1.10",
        :L_PAYMENTREQUEST_0_QTY1 => 3,
        :L_SURVEYCHOICE0 => 'abcd' # Note the 'n' was replaced by the index
      }
    end

    it 'should handle Recurring Payment parameters' do
      recurring_request.to_params.should == {
        :PAYMENTREQUEST_0_AMT => "0.00",
        :PAYMENTREQUEST_0_TAXAMT => "0.00",
        :PAYMENTREQUEST_0_SHIPPINGAMT => "0.00",
        :PAYMENTREQUEST_0_CURRENCYCODE => :JPY,
        :L_BILLINGTYPE0 => :RecurringPayments,
        :L_BILLINGAGREEMENTDESCRIPTION0 => "Recurring Payment Request"
      }
    end

    it 'should handle Reference Transactions parameters' do
      reference_transaction_request.to_params.should == {
        :PAYMENTREQUEST_0_AMT => "0.00",
        :PAYMENTREQUEST_0_TAXAMT => "0.00",
        :PAYMENTREQUEST_0_SHIPPINGAMT => "0.00",
        :PAYMENTREQUEST_0_CURRENCYCODE => :JPY,
        :L_BILLINGTYPE0 => :MerchantInitiatedBillingSingleAgreement,
        :L_BILLINGAGREEMENTDESCRIPTION0 => "Reference Transaction Request"
      }
    end
  end

  describe '#items_amount' do
    context 'when BigDecimal'
    let(:instance) do
      Paypal::Payment::Request.new(
        :items => [{
          :quantity => 3,
          :name => 'Item1',
          :description => 'Awesome Item 1!',
          :amount => 130.45
        }]
      )
    end

    # NOTE:
    # 130.45 * 3 => 391.34999999999997 (in ruby 1.9)
    it 'should calculate total amount correctly' do
      instance.items_amount.should == 391.35
    end
  end
end


================================================
FILE: spec/paypal/payment/response/address_spec.rb
================================================
require 'spec_helper.rb'

describe Paypal::Payment::Response::Address do
  let :keys do
    Paypal::Payment::Response::Address.optional_attributes
  end

  describe '.new' do
    it 'should allow nil for attributes' do
      payer = Paypal::Payment::Response::Address.new
      keys.each do |key|
        payer.send(key).should be_nil
      end
    end

    it 'should treat all attributes as String' do
      attributes = keys.inject({}) do |attributes, key|
        attributes.merge!(key => "xyz")
      end
      payer = Paypal::Payment::Response::Address.new attributes
      keys.each do |key|
        payer.send(key).should == "xyz"
      end
    end
  end
end

================================================
FILE: spec/paypal/payment/response/info_spec.rb
================================================
require 'spec_helper.rb'

describe Paypal::Payment::Response::Info do
  let :attribute_mapping do
    Paypal::Payment::Response::Info.attribute_mapping
  end

  let :attributes do
    {
      :ACK => 'Success',
      :CURRENCYCODE => 'JPY',
      :ERRORCODE => 0,
      :ORDERTIME => '2011-02-08T03:23:54Z',
      :PAYMENTSTATUS => 'Completed',
      :PAYMENTTYPE => 'instant',
      :PENDINGREASON => 'None',
      :PROTECTIONELIGIBILITY => 'Ineligible',
      :PROTECTIONELIGIBILITYTYPE => 'None',
      :REASONCODE => 'None',
      :TRANSACTIONID => '8NC65222871997739',
      :TRANSACTIONTYPE => 'expresscheckout',
      :AMT => '14.00',
      :FEEAMT => '0.85',
      :TAXAMT => '0.00',
      :RECEIPTID => '12345',
      :SECUREMERCHANTACCOUNTID => '123456789',
      :PAYMENTREQUESTID => '12345',
      :SELLERPAYPALACCOUNTID => 'seller@shop.example.com',
      :EXCHANGERATE => '0.811965'
    }
  end

  describe '.new' do
    context 'when attribute keys are uppercase Symbol' do
      it 'should accept all without any warning' do
        Paypal.logger.should_not_receive(:warn)
        from_symbol_uppercase = Paypal::Payment::Response::Info.new attributes
        attribute_mapping.values.each do |key|
          from_symbol_uppercase.send(key).should_not be_nil
        end
        from_symbol_uppercase.amount.should == Paypal::Payment::Common::Amount.new(
          :total => 14,
          :fee => 0.85
        )
      end
    end

    context 'when attribute keys are lowercase Symbol' do
      it 'should ignore them and warn' do
        _attrs_ = attributes.inject({}) do |_attrs_, (k, v)|
          _attrs_.merge!(k.to_s.downcase.to_sym => v)
        end
        _attrs_.each do |key, value|
          Paypal.logger.should_receive(:warn).with(
            "Ignored Parameter (Paypal::Payment::Response::Info): #{key}=#{value}"
          )
        end
        from_symbol_lowercase = Paypal::Payment::Response::Info.new _attrs_
        attribute_mapping.values.each do |key|
          from_symbol_lowercase.send(key).should be_nil
        end
        from_symbol_lowercase.amount.should == Paypal::Payment::Common::Amount.new
      end
    end

    context 'when attribute keys are String' do
      it 'should ignore them and warn' do
        attributes.stringify_keys.each do |key, value|
          Paypal.logger.should_receive(:warn).with(
            "Ignored Parameter (Paypal::Payment::Response::Info): #{key}=#{value}"
          )
        end
        from_string = Paypal::Payment::Response::Info.new attributes.stringify_keys
        attribute_mapping.values.each do |key|
          from_string.send(key).should be_nil
        end
        from_string.amount.should == Paypal::Payment::Common::Amount.new
      end
    end

    context 'when non-supported attributes are given' do
      it 'should ignore them and warn' do
        _attr_ = attributes.merge(
          :ignored => 'Ignore me!'
        )
        Paypal.logger.should_receive(:warn).with(
          "Ignored Parameter (Paypal::Payment::Response::Info): ignored=Ignore me!"
        )
        Paypal::Payment::Response::Info.new _attr_
      end
    end
  end
end


================================================
FILE: spec/paypal/payment/response/item_spec.rb
================================================
require 'spec_helper.rb'

describe Paypal::Payment::Response::Info do
  let :attributes do
    {
      :NAME => 'Item Name',
      :DESC => 'Item Description',
      :QTY => '1',
      :NUMBER => '1',
      :ITEMCATEGORY => 'Digital',
      :ITEMWIDTHVALUE => '1.0',
      :ITEMHEIGHTVALUE => '2.0',
      :ITEMLENGTHVALUE => '3.0',
      :ITEMWEIGHTVALUE => '4.0'
    }
  end

  describe '.new' do
    subject { Paypal::Payment::Response::Item.new(attributes) }
    its(:name) { should == 'Item Name' }
    its(:description) { should == 'Item Description' }
    its(:quantity) { should == 1 }
    its(:category) { should == 'Digital' }
    its(:width) { should == '1.0' }
    its(:height) { should == '2.0' }
    its(:length) { should == '3.0' }
    its(:weight) { should == '4.0' }
    its(:number) { should == '1' }

    context 'when non-supported attributes are given' do
      it 'should ignore them and warn' do
        _attr_ = attributes.merge(
          :ignored => 'Ignore me!'
        )
        Paypal.logger.should_receive(:warn).with(
          "Ignored Parameter (Paypal::Payment::Response::Item): ignored=Ignore me!"
        )
        Paypal::Payment::Response::Item.new _attr_
      end
    end
  end
end

================================================
FILE: spec/paypal/payment/response/payer_spec.rb
================================================
require 'spec_helper.rb'

describe Paypal::Payment::Response::Payer do
  let :keys do
    Paypal::Payment::Response::Payer.optional_attributes
  end

  describe '.new' do
    it 'should allow nil for attributes' do
      payer = Paypal::Payment::Response::Payer.new
      keys.each do |key|
        payer.send(key).should be_nil
      end
    end

    it 'should treat all attributes as String' do
      attributes = keys.inject({}) do |attributes, key|
        attributes.merge!(key => "xyz")
      end
      payer = Paypal::Payment::Response::Payer.new attributes
      keys.each do |key|
        payer.send(key).should == "xyz"
      end
    end
  end
end

================================================
FILE: spec/paypal/payment/response_spec.rb
================================================
require 'spec_helper.rb'

describe Paypal::Payment::Response do
  describe '.new' do
    context 'when non-supported attributes are given' do
      it 'should ignore them and warn' do
        Paypal.logger.should_receive(:warn).with(
          "Ignored Parameter (Paypal::Payment::Response): ignored=Ignore me!"
        )
        response = Paypal::Payment::Response.new(
          :ignored => 'Ignore me!'
        )
      end
    end
  end
end

================================================
FILE: spec/paypal/util_spec.rb
================================================
require 'spec_helper.rb'

describe Paypal::Util do
  describe '.formatted_amount' do
    it 'should return String in "xx.yy" format' do
      Paypal::Util.formatted_amount(nil).should == '0.00'
      Paypal::Util.formatted_amount(10).should == '10.00'
      Paypal::Util.formatted_amount(10.02).should == '10.02'
      Paypal::Util.formatted_amount(10.2).should == '10.20'
      Paypal::Util.formatted_amount(10.24).should == '10.24'
      Paypal::Util.formatted_amount(10.255).should == '10.25'
    end
  end

  describe '.to_numeric' do
    it 'should return Numeric' do
      Paypal::Util.to_numeric('10').should be_kind_of(Integer)
      Paypal::Util.to_numeric('10.5').should be_kind_of(Float)
      Paypal::Util.to_numeric('-1.5').should == -1.5
      Paypal::Util.to_numeric('-1').should == -1
      Paypal::Util.to_numeric('0').should == 0
      Paypal::Util.to_numeric('0.00').should == 0
      Paypal::Util.to_numeric('10').should == 10
      Paypal::Util.to_numeric('10.00').should == 10
      Paypal::Util.to_numeric('10.02').should == 10.02
      Paypal::Util.to_numeric('10.2').should == 10.2
      Paypal::Util.to_numeric('10.20').should == 10.2
      Paypal::Util.to_numeric('10.24').should == 10.24
      Paypal::Util.to_numeric('10.25').should == 10.25
    end
  end
end

================================================
FILE: spec/spec_helper.rb
================================================
require 'simplecov'

SimpleCov.start do
  add_filter 'spec'
end

require 'paypal'
require 'rspec'
require 'helpers/fake_response_helper'

RSpec.configure do |config|
  config.before do
    Paypal.logger = double("logger")
  end
  config.after do
    FakeWeb.clean_registry
  end
end

def sandbox_mode(&block)
  Paypal.sandbox!
  yield
ensure
  Paypal.sandbox = false
end
Download .txt
gitextract_wg6sknv5/

├── .document
├── .github/
│   └── FUNDING.yml
├── .gitignore
├── .rspec
├── .travis.yml
├── Gemfile
├── LICENSE
├── README.rdoc
├── Rakefile
├── VERSION
├── lib/
│   ├── paypal/
│   │   ├── base.rb
│   │   ├── exception/
│   │   │   ├── api_error.rb
│   │   │   └── http_error.rb
│   │   ├── exception.rb
│   │   ├── express/
│   │   │   ├── request.rb
│   │   │   └── response.rb
│   │   ├── express.rb
│   │   ├── ipn.rb
│   │   ├── nvp/
│   │   │   ├── request.rb
│   │   │   └── response.rb
│   │   ├── payment/
│   │   │   ├── common/
│   │   │   │   └── amount.rb
│   │   │   ├── recurring/
│   │   │   │   ├── activation.rb
│   │   │   │   ├── billing.rb
│   │   │   │   └── summary.rb
│   │   │   ├── recurring.rb
│   │   │   ├── request/
│   │   │   │   └── item.rb
│   │   │   ├── request.rb
│   │   │   ├── response/
│   │   │   │   ├── address.rb
│   │   │   │   ├── info.rb
│   │   │   │   ├── item.rb
│   │   │   │   ├── payer.rb
│   │   │   │   ├── reference.rb
│   │   │   │   └── refund.rb
│   │   │   └── response.rb
│   │   └── util.rb
│   └── paypal.rb
├── paypal-express.gemspec
└── spec/
    ├── fake_response/
    │   ├── BillAgreementUpdate/
    │   │   ├── fetch.txt
    │   │   └── revoke.txt
    │   ├── CreateBillingAgreement/
    │   │   └── success.txt
    │   ├── CreateRecurringPaymentsProfile/
    │   │   ├── failure.txt
    │   │   └── success.txt
    │   ├── DoCapture/
    │   │   ├── failure.txt
    │   │   └── success.txt
    │   ├── DoExpressCheckoutPayment/
    │   │   ├── failure.txt
    │   │   ├── success.txt
    │   │   ├── success_with_billing_agreement.txt
    │   │   └── success_with_many_items.txt
    │   ├── DoReferenceTransaction/
    │   │   ├── failure.txt
    │   │   └── success.txt
    │   ├── DoVoid/
    │   │   └── success.txt
    │   ├── GetExpressCheckoutDetails/
    │   │   ├── failure.txt
    │   │   ├── success.txt
    │   │   └── with_billing_accepted_status.txt
    │   ├── GetRecurringPaymentsProfileDetails/
    │   │   ├── failure.txt
    │   │   └── success.txt
    │   ├── GetTransactionDetails/
    │   │   ├── failure.txt
    │   │   └── success.txt
    │   ├── IPN/
    │   │   ├── invalid.txt
    │   │   └── valid.txt
    │   ├── ManageRecurringPaymentsProfileStatus/
    │   │   ├── failure.txt
    │   │   └── success.txt
    │   ├── RefundTransaction/
    │   │   └── full.txt
    │   └── SetExpressCheckout/
    │       ├── failure.txt
    │       └── success.txt
    ├── helpers/
    │   └── fake_response_helper.rb
    ├── paypal/
    │   ├── exception/
    │   │   ├── api_error_spec.rb
    │   │   └── http_error_spec.rb
    │   ├── express/
    │   │   ├── request_spec.rb
    │   │   └── response_spec.rb
    │   ├── ipn_spec.rb
    │   ├── nvp/
    │   │   ├── request_spec.rb
    │   │   └── response_spec.rb
    │   ├── payment/
    │   │   ├── common/
    │   │   │   └── amount_spec.rb
    │   │   ├── recurring/
    │   │   │   └── activation_spec.rb
    │   │   ├── recurring_spec.rb
    │   │   ├── request/
    │   │   │   └── item_spec.rb
    │   │   ├── request_spec.rb
    │   │   ├── response/
    │   │   │   ├── address_spec.rb
    │   │   │   ├── info_spec.rb
    │   │   │   ├── item_spec.rb
    │   │   │   └── payer_spec.rb
    │   │   └── response_spec.rb
    │   └── util_spec.rb
    └── spec_helper.rb
Download .txt
SYMBOL INDEX (144 symbols across 28 files)

FILE: lib/paypal.rb
  type Paypal (line 8) | module Paypal
    function endpoint (line 21) | def self.endpoint
    function popup_endpoint (line 28) | def self.popup_endpoint
    function log (line 36) | def self.log(message, mode = :info)
    function logger (line 39) | def self.logger
    function logger= (line 42) | def self.logger=(logger)
    function sandbox? (line 48) | def self.sandbox?
    function sandbox! (line 51) | def self.sandbox!
    function sandbox= (line 54) | def self.sandbox=(boolean)

FILE: lib/paypal/base.rb
  type Paypal (line 1) | module Paypal
    class Base (line 2) | class Base
      method initialize (line 5) | def initialize(attributes = {})

FILE: lib/paypal/exception.rb
  type Paypal (line 1) | module Paypal
    class Exception (line 2) | class Exception < StandardError

FILE: lib/paypal/exception/api_error.rb
  type Paypal (line 1) | module Paypal
    class Exception (line 2) | class Exception
      class APIError (line 3) | class APIError < Exception
        method initialize (line 5) | def initialize(response = {})
        method message (line 13) | def message
        class Response (line 22) | class Response
          class Detail (line 41) | class Detail
            method initialize (line 51) | def initialize(attributes = {})
          method initialize (line 65) | def initialize(attributes = {})
          method short_messages (line 88) | def short_messages

FILE: lib/paypal/exception/http_error.rb
  type Paypal (line 1) | module Paypal
    class Exception (line 2) | class Exception
      class HttpError (line 3) | class HttpError < Exception
        method initialize (line 5) | def initialize(code, message, body = '')

FILE: lib/paypal/express/request.rb
  type Paypal (line 1) | module Paypal
    type Express (line 2) | module Express
      class Request (line 3) | class Request < NVP::Request
        method setup (line 7) | def setup(payment_requests, return_url, cancel_url, options = {})
        method details (line 38) | def details(token)
        method transaction_details (line 43) | def transaction_details(transaction_id)
        method checkout! (line 48) | def checkout!(token, payer_id, payment_requests)
        method capture! (line 60) | def capture!(authorization_id, amount, currency_code, complete_typ...
        method void! (line 72) | def void!(authorization_id, params={})
        method subscribe! (line 84) | def subscribe!(token, recurring_profile)
        method subscription (line 93) | def subscription(profile_id)
        method renew! (line 101) | def renew!(profile_id, action, options = {})
        method suspend! (line 113) | def suspend!(profile_id, options = {})
        method cancel! (line 117) | def cancel!(profile_id, options = {})
        method reactivate! (line 121) | def reactivate!(profile_id, options = {})
        method agree! (line 128) | def agree!(token, options = {})
        method agreement (line 139) | def agreement(reference_id)
        method charge! (line 147) | def charge!(reference_id, amount, options = {})
        method revoke! (line 160) | def revoke!(reference_id)
        method refund! (line 172) | def refund!(transaction_id, options = {})

FILE: lib/paypal/express/response.rb
  type Paypal (line 1) | module Paypal
    type Express (line 2) | module Express
      class Response (line 3) | class Response < NVP::Response
        method initialize (line 6) | def initialize(response, options = {})
        method redirect_uri (line 12) | def redirect_uri
        method popup_uri (line 18) | def popup_uri
        method query (line 26) | def query(with_cmd = false)

FILE: lib/paypal/ipn.rb
  type Paypal (line 1) | module Paypal
    type IPN (line 2) | module IPN
      function endpoint (line 3) | def self.endpoint
      function verify! (line 11) | def self.verify!(raw_post)

FILE: lib/paypal/nvp/request.rb
  type Paypal (line 1) | module Paypal
    type NVP (line 2) | module NVP
      class Request (line 3) | class Request < Base
        method endpoint (line 13) | def self.endpoint
        method initialize (line 21) | def initialize(attributes = {})
        method common_params (line 26) | def common_params
        method request (line 36) | def request(method, params = {})
        method post (line 44) | def post(method, params)
        method handle_response (line 48) | def handle_response

FILE: lib/paypal/nvp/response.rb
  type Paypal (line 1) | module Paypal
    type NVP (line 2) | module NVP
      class Response (line 3) | class Response < Base
        method initialize (line 45) | def initialize(attributes = {})

FILE: lib/paypal/payment/common/amount.rb
  type Paypal (line 1) | module Paypal
    type Payment (line 2) | module Payment
      type Common (line 3) | module Common
        class Amount (line 4) | class Amount < Base
          method numeric_attribute? (line 7) | def numeric_attribute?(key)

FILE: lib/paypal/payment/recurring.rb
  type Paypal (line 1) | module Paypal
    type Payment (line 2) | module Payment
      class Recurring (line 3) | class Recurring < Base
        method initialize (line 7) | def initialize(attributes = {})
        method to_params (line 15) | def to_params
        method numeric_attribute? (line 38) | def numeric_attribute?(key)

FILE: lib/paypal/payment/recurring/activation.rb
  type Paypal (line 1) | module Paypal
    type Payment (line 2) | module Payment
      class Recurring::Activation (line 3) | class Recurring::Activation < Base
        method to_params (line 6) | def to_params

FILE: lib/paypal/payment/recurring/billing.rb
  type Paypal (line 1) | module Paypal
    type Payment (line 2) | module Payment
      class Recurring::Billing (line 3) | class Recurring::Billing < Base
        method initialize (line 7) | def initialize(attributes = {})
        method to_params (line 21) | def to_params

FILE: lib/paypal/payment/recurring/summary.rb
  type Paypal (line 1) | module Paypal
    type Payment (line 2) | module Payment
      class Recurring::Summary (line 3) | class Recurring::Summary < Base
        method numeric_attribute? (line 6) | def numeric_attribute?(key)

FILE: lib/paypal/payment/request.rb
  type Paypal (line 1) | module Paypal
    type Payment (line 2) | module Payment
      class Request (line 3) | class Request < Base
        method initialize (line 7) | def initialize(attributes = {})
        method to_params (line 25) | def to_params(index = 0)
        method items_amount (line 60) | def items_amount

FILE: lib/paypal/payment/request/item.rb
  type Paypal (line 1) | module Paypal
    type Payment (line 2) | module Payment
      class Request::Item (line 3) | class Request::Item < Base
        method initialize (line 6) | def initialize(attributes = {})
        method to_params (line 11) | def to_params(parent_index, index = 0)

FILE: lib/paypal/payment/response.rb
  type Paypal (line 1) | module Paypal
    type Payment (line 2) | module Payment
      class Response (line 3) | class Response < Base
        method initialize (line 6) | def initialize(attributes = {})

FILE: lib/paypal/payment/response/address.rb
  type Paypal (line 1) | module Paypal
    type Payment (line 2) | module Payment
      class Response::Address (line 3) | class Response::Address < Base

FILE: lib/paypal/payment/response/info.rb
  type Paypal (line 1) | module Paypal
    type Payment (line 2) | module Payment
      class Response::Info (line 3) | class Response::Info < Base
        method initialize (line 27) | def initialize(attributes = {})

FILE: lib/paypal/payment/response/item.rb
  type Paypal (line 1) | module Paypal
    type Payment (line 2) | module Payment
      class Response::Item (line 3) | class Response::Item < Base
        method initialize (line 23) | def initialize(attributes = {})

FILE: lib/paypal/payment/response/payer.rb
  type Paypal (line 1) | module Paypal
    type Payment (line 2) | module Payment
      class Response::Payer (line 3) | class Response::Payer < Base

FILE: lib/paypal/payment/response/reference.rb
  type Paypal (line 1) | module Paypal
    type Payment (line 2) | module Payment
      class Response::Reference (line 3) | class Response::Reference < Base

FILE: lib/paypal/payment/response/refund.rb
  type Paypal (line 1) | module Paypal
    type Payment (line 2) | module Payment
      class Response::Refund (line 3) | class Response::Refund < Base
        method initialize (line 7) | def initialize(attributes = {})

FILE: lib/paypal/util.rb
  type Paypal (line 1) | module Paypal
    type Util (line 2) | module Util
      function formatted_amount (line 4) | def self.formatted_amount(x)
      function to_numeric (line 9) | def self.to_numeric(x)
      function numeric_attribute? (line 17) | def numeric_attribute?(key)
      function == (line 21) | def ==(other)

FILE: spec/helpers/fake_response_helper.rb
  type FakeResponseHelper (line 3) | module FakeResponseHelper
    function fake_response (line 5) | def fake_response(file_path, api = :NVP, options = {})
    function request_to (line 23) | def request_to(endpoint, method = :get)

FILE: spec/paypal/express/request_spec.rb
  class Paypal::Express::Request (line 4) | class Paypal::Express::Request
    method post_with_logging (line 6) | def post_with_logging(method, params)

FILE: spec/spec_helper.rb
  function sandbox_mode (line 20) | def sandbox_mode(&block)
Condensed preview — 85 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (131K chars).
[
  {
    "path": ".document",
    "chars": 60,
    "preview": "README.rdoc\nlib/**/*.rb\nbin/*\nfeatures/**/*.feature\nLICENSE\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 59,
    "preview": "# These are supported funding model platforms\n\ngithub: nov\n"
  },
  {
    "path": ".gitignore",
    "chars": 160,
    "preview": "## MAC OS\n.DS_Store\n\n## TEXTMATE\n*.tmproj\ntmtags\n\n## EMACS\n*~\n\\#*\n.\\#*\n\n## VIM\n*.swp\n\n## PROJECT::GENERAL\ncoverage*\nrdoc"
  },
  {
    "path": ".rspec",
    "chars": 31,
    "preview": "--color\n--format=documentation\n"
  },
  {
    "path": ".travis.yml",
    "chars": 65,
    "preview": "before_install:\n  - gem install bundler\n\nrvm:\n  - 2.0.0\n  - 2.1.2"
  },
  {
    "path": "Gemfile",
    "chars": 93,
    "preview": "source 'http://rubygems.org'\n\nplatform :jruby do\n  gem 'jruby-openssl', '>= 0.7'\nend\n\ngemspec"
  },
  {
    "path": "LICENSE",
    "chars": 1054,
    "preview": "Copyright (c) 2011 nov matake\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this soft"
  },
  {
    "path": "README.rdoc",
    "chars": 1006,
    "preview": "= paypal-express\n\nHandle PayPal Express Checkout.\nBoth Instance Payment and Recurring Payment are supported.\nExpress Che"
  },
  {
    "path": "Rakefile",
    "chars": 402,
    "preview": "require 'bundler'\nBundler::GemHelper.install_tasks\n\nrequire 'rspec/core/rake_task'\nRSpec::Core::RakeTask.new(:spec)\n\nnam"
  },
  {
    "path": "VERSION",
    "chars": 5,
    "preview": "0.8.1"
  },
  {
    "path": "lib/paypal/base.rb",
    "chars": 450,
    "preview": "module Paypal\n  class Base\n    include AttrRequired, AttrOptional, Util\n\n    def initialize(attributes = {})\n      if at"
  },
  {
    "path": "lib/paypal/exception/api_error.rb",
    "chars": 2893,
    "preview": "module Paypal\n  class Exception\n    class APIError < Exception\n      attr_accessor :response\n      def initialize(respon"
  },
  {
    "path": "lib/paypal/exception/http_error.rb",
    "chars": 250,
    "preview": "module Paypal\n  class Exception\n    class HttpError < Exception\n      attr_accessor :code, :message, :body\n      def ini"
  },
  {
    "path": "lib/paypal/exception.rb",
    "chars": 57,
    "preview": "module Paypal\n  class Exception < StandardError\n  end\nend"
  },
  {
    "path": "lib/paypal/express/request.rb",
    "chars": 5568,
    "preview": "module Paypal\n  module Express\n    class Request < NVP::Request\n\n      # Common\n\n      def setup(payment_requests, retur"
  },
  {
    "path": "lib/paypal/express/response.rb",
    "chars": 926,
    "preview": "module Paypal\n  module Express\n    class Response < NVP::Response\n      attr_accessor :pay_on_paypal, :mobile\n\n      def"
  },
  {
    "path": "lib/paypal/express.rb",
    "chars": 16,
    "preview": "require 'paypal'"
  },
  {
    "path": "lib/paypal/ipn.rb",
    "chars": 455,
    "preview": "module Paypal\n  module IPN\n    def self.endpoint\n      _endpoint_ = URI.parse Paypal.endpoint\n      _endpoint_.query = {"
  },
  {
    "path": "lib/paypal/nvp/request.rb",
    "chars": 1548,
    "preview": "module Paypal\n  module NVP\n    class Request < Base\n      attr_required :username, :password, :signature\n      attr_opti"
  },
  {
    "path": "lib/paypal/nvp/response.rb",
    "chars": 10165,
    "preview": "module Paypal\n  module NVP\n    class Response < Base\n      cattr_reader :attribute_mapping\n      @@attribute_mapping = {"
  },
  {
    "path": "lib/paypal/payment/common/amount.rb",
    "chars": 274,
    "preview": "module Paypal\n  module Payment\n    module Common\n      class Amount < Base\n        attr_optional :total, :item, :fee, :h"
  },
  {
    "path": "lib/paypal/payment/recurring/activation.rb",
    "chars": 309,
    "preview": "module Paypal\n  module Payment\n    class Recurring::Activation < Base\n      attr_optional :initial_amount, :failed_actio"
  },
  {
    "path": "lib/paypal/payment/recurring/billing.rb",
    "chars": 1287,
    "preview": "module Paypal\n  module Payment\n    class Recurring::Billing < Base\n      attr_optional :period, :frequency, :paid, :curr"
  },
  {
    "path": "lib/paypal/payment/recurring/summary.rb",
    "chars": 354,
    "preview": "module Paypal\n  module Payment\n    class Recurring::Summary < Base\n      attr_optional :next_billing_date, :cycles_compl"
  },
  {
    "path": "lib/paypal/payment/recurring.rb",
    "chars": 1508,
    "preview": "module Paypal\n  module Payment\n    class Recurring < Base\n      attr_optional :start_date, :description, :identifier, :s"
  },
  {
    "path": "lib/paypal/payment/request/item.rb",
    "chars": 942,
    "preview": "module Paypal\n  module Payment\n    class Request::Item < Base\n      attr_optional :name, :description, :amount, :number,"
  },
  {
    "path": "lib/paypal/payment/request.rb",
    "chars": 2730,
    "preview": "module Paypal\n  module Payment\n    class Request < Base\n      attr_optional :action, :currency_code, :description, :noti"
  },
  {
    "path": "lib/paypal/payment/response/address.rb",
    "chars": 196,
    "preview": "module Paypal\n  module Payment\n    class Response::Address < Base\n      attr_optional :owner, :status, :name, :zip, :str"
  },
  {
    "path": "lib/paypal/payment/response/info.rb",
    "chars": 1493,
    "preview": "module Paypal\n  module Payment\n    class Response::Info < Base\n      cattr_reader :attribute_mapping\n      @@attribute_m"
  },
  {
    "path": "lib/paypal/payment/response/item.rb",
    "chars": 1153,
    "preview": "module Paypal\n  module Payment\n    class Response::Item < Base\n      cattr_reader :attribute_mapping\n      @@attribute_m"
  },
  {
    "path": "lib/paypal/payment/response/payer.rb",
    "chars": 165,
    "preview": "module Paypal\n  module Payment\n    class Response::Payer < Base\n      attr_optional :identifier, :status, :first_name, :"
  },
  {
    "path": "lib/paypal/payment/response/reference.rb",
    "chars": 185,
    "preview": "module Paypal\n  module Payment\n    class Response::Reference < Base\n      attr_required :identifier\n      attr_optional "
  },
  {
    "path": "lib/paypal/payment/response/refund.rb",
    "chars": 267,
    "preview": "module Paypal\n  module Payment\n    class Response::Refund < Base\n      attr_optional :transaction_id\n      attr_accessor"
  },
  {
    "path": "lib/paypal/payment/response.rb",
    "chars": 3002,
    "preview": "module Paypal\n  module Payment\n    class Response < Base\n      attr_accessor :amount, :ship_to, :bill_to, :description, "
  },
  {
    "path": "lib/paypal/util.rb",
    "chars": 518,
    "preview": "module Paypal\n  module Util\n\n    def self.formatted_amount(x)\n      # Thanks @nahi ;)\n      sprintf \"%0.2f\", BigDecimal."
  },
  {
    "path": "lib/paypal.rb",
    "chars": 2049,
    "preview": "require 'logger'\nrequire 'active_support'\nrequire 'active_support/core_ext'\nrequire 'attr_required'\nrequire 'attr_option"
  },
  {
    "path": "paypal-express.gemspec",
    "chars": 1191,
    "preview": "Gem::Specification.new do |s|\n  s.name = \"paypal-express\"\n  s.version = File.read(File.join(File.dirname(__FILE__), \"VER"
  },
  {
    "path": "spec/fake_response/BillAgreementUpdate/fetch.txt",
    "chars": 369,
    "preview": "BILLINGAGREEMENTID=B%2d8XW15926RT2253736&BILLINGAGREEMENTDESCRIPTION=Reference%20Payment%20Agreement&BILLINGAGREEMENTSTA"
  },
  {
    "path": "spec/fake_response/BillAgreementUpdate/revoke.txt",
    "chars": 369,
    "preview": "BILLINGAGREEMENTID=B%2d8XW15926RT2253736&BILLINGAGREEMENTDESCRIPTION=Reference%20Payment%20Agreement&BILLINGAGREEMENTSTA"
  },
  {
    "path": "spec/fake_response/CreateBillingAgreement/success.txt",
    "chars": 148,
    "preview": "BILLINGAGREEMENTID=B%2d45776062X95244343&TIMESTAMP=2011%2d08%2d06T07%3a53%3a50Z&CORRELATIONID=9a16fde4456e1&ACK=Success&"
  },
  {
    "path": "spec/fake_response/CreateRecurringPaymentsProfile/failure.txt",
    "chars": 222,
    "preview": "TIMESTAMP=2011%2d02%2d08T07%3a18%3a29Z&CORRELATIONID=fde1527b25b4f&ACK=Failure&VERSION=66%2e0&BUILD=1704252&L_ERRORCODE0"
  },
  {
    "path": "spec/fake_response/CreateRecurringPaymentsProfile/success.txt",
    "chars": 162,
    "preview": "PROFILEID=I%2dL8N58XFUCET3&PROFILESTATUS=ActiveProfile&TIMESTAMP=2011%2d02%2d08T07%3a17%3a51Z&CORRELATIONID=7d2e125f5ca6"
  },
  {
    "path": "spec/fake_response/DoCapture/failure.txt",
    "chars": 224,
    "preview": "ACK=Failure&BUILD=8334781&CORRELATIONID=dd9e16358819b&L_ERRORCODE0=10609&L_LONGMESSAGE0=Transaction+id+is+invalid.&L_SEV"
  },
  {
    "path": "spec/fake_response/DoCapture/success.txt",
    "chars": 508,
    "preview": "ACK=Success&AMT=440.00&AUTHORIZATIONID=2RG78938NK8989844&BUILD=8334781&CORRELATIONID=311611c111b48&CURRENCYCODE=BRL&EXCH"
  },
  {
    "path": "spec/fake_response/DoExpressCheckoutPayment/failure.txt",
    "chars": 214,
    "preview": "TIMESTAMP=2011%2d02%2d08T07%3a15%3a19Z&CORRELATIONID=5dd0308345382&ACK=Failure&VERSION=66%2e0&BUILD=1721431&L_ERRORCODE0"
  },
  {
    "path": "spec/fake_response/DoExpressCheckoutPayment/success.txt",
    "chars": 742,
    "preview": "TOKEN=EC-9E2743126S4330617&SUCCESSPAGEREDIRECTREQUESTED=false&TIMESTAMP=2011-02-08T03:23:55Z&CORRELATIONID=15b93874c358c"
  },
  {
    "path": "spec/fake_response/DoExpressCheckoutPayment/success_with_billing_agreement.txt",
    "chars": 781,
    "preview": "TOKEN=EC-9E2743126S4330617&SUCCESSPAGEREDIRECTREQUESTED=false&TIMESTAMP=2011-02-08T03:23:55Z&CORRELATIONID=15b93874c358c"
  },
  {
    "path": "spec/fake_response/DoExpressCheckoutPayment/success_with_many_items.txt",
    "chars": 13821,
    "preview": "TOKEN=EC%2d7HG122968U843864S&CHECKOUTSTATUS=PaymentActionNotInitiated&TIMESTAMP=2011%2d04%2d12T03%3a51%3a58Z&CORRELATION"
  },
  {
    "path": "spec/fake_response/DoReferenceTransaction/failure.txt",
    "chars": 447,
    "preview": "TIMESTAMP=2011%2d07%2d19T08%3a59%3a52Z&CORRELATIONID=ed8096a3c3d7d&ACK=Failure&VERSION=72%2e0&BUILD=1982348&L_ERRORCODE0"
  },
  {
    "path": "spec/fake_response/DoReferenceTransaction/success.txt",
    "chars": 433,
    "preview": "BILLINGAGREEMENTID=B%2d7HU5U5U1ULU14U49E&TIMESTAMP=2011%2d07%2d19T08%3a30%3a08Z&CORRELATIONID=12ca85fb3c9d2&ACK=Success&"
  },
  {
    "path": "spec/fake_response/DoVoid/success.txt",
    "chars": 90,
    "preview": "ACK=Success&AUTHORIZATIONID=1a2b3c4d5e6f&BUILD=13055236&TIMESTAMP=2014-10-03T17%3A34%3A56Z"
  },
  {
    "path": "spec/fake_response/GetExpressCheckoutDetails/failure.txt",
    "chars": 1532,
    "preview": "TOKEN=EC%2d9E2743126S4330617&CHECKOUTSTATUS=PaymentActionCompleted&TIMESTAMP=2011%2d02%2d08T07%3a10%3a26Z&CORRELATIONID="
  },
  {
    "path": "spec/fake_response/GetExpressCheckoutDetails/success.txt",
    "chars": 1589,
    "preview": "TOKEN=EC%2d7HG122968U843864S&CHECKOUTSTATUS=PaymentActionNotInitiated&TIMESTAMP=2011%2d04%2d12T03%3a51%3a58Z&CORRELATION"
  },
  {
    "path": "spec/fake_response/GetExpressCheckoutDetails/with_billing_accepted_status.txt",
    "chars": 715,
    "preview": "TOKEN=EC%2d4D299105F9409730D&BILLINGAGREEMENTACCEPTEDSTATUS=1&CHECKOUTSTATUS=PaymentActionNotInitiated&TIMESTAMP=2011%2d"
  },
  {
    "path": "spec/fake_response/GetRecurringPaymentsProfileDetails/failure.txt",
    "chars": 264,
    "preview": "PROFILEID=I%2dK1VFLRN3VVG0x&TIMESTAMP=2011%2d02%2d08T07%3a19%3a43Z&CORRELATIONID=edc954f1c34c5&ACK=Failure&VERSION=66%2e"
  },
  {
    "path": "spec/fake_response/GetRecurringPaymentsProfileDetails/success.txt",
    "chars": 1107,
    "preview": "PROFILEID=I%2dK1VFLRN3VVG0&STATUS=Active&AUTOBILLOUTAMT=NoAutoBill&DESC=iKnow%21%20Subscription&MAXFAILEDPAYMENTS=0&SUBS"
  },
  {
    "path": "spec/fake_response/GetTransactionDetails/failure.txt",
    "chars": 418,
    "preview": "ADDRESSOWNER=PayPal&ADDRESSSTATUS=None&TIMESTAMP=2012%2d02%2d23T21%3a07%3a22Z&CORRELATIONID=4e18d75a23953&ACK=Failure&VE"
  },
  {
    "path": "spec/fake_response/GetTransactionDetails/success.txt",
    "chars": 1102,
    "preview": "RECEIVEREMAIL=test%40email%2ecom&RECEIVERID=ABCDEFG12345&EMAIL=test%40email%2ecom&PAYERID=12345ABCDEFG&PAYERSTATUS=verif"
  },
  {
    "path": "spec/fake_response/IPN/invalid.txt",
    "chars": 7,
    "preview": "INVALID"
  },
  {
    "path": "spec/fake_response/IPN/valid.txt",
    "chars": 8,
    "preview": "VERIFIED"
  },
  {
    "path": "spec/fake_response/ManageRecurringPaymentsProfileStatus/failure.txt",
    "chars": 387,
    "preview": "TIMESTAMP=2011%2d02%2d08T07%3a21%3a08Z&CORRELATIONID=aa160cad77481&ACK=Failure&VERSION=66%2e0&BUILD=1704252&L_ERRORCODE0"
  },
  {
    "path": "spec/fake_response/ManageRecurringPaymentsProfileStatus/success.txt",
    "chars": 134,
    "preview": "PROFILEID=I%2dK1VFLRN3VVG0&TIMESTAMP=2011%2d02%2d08T07%3a20%3a44Z&CORRELATIONID=86bde487ccba6&ACK=Success&VERSION=66%2e0"
  },
  {
    "path": "spec/fake_response/RefundTransaction/full.txt",
    "chars": 253,
    "preview": "REFUNDTRANSACTIONID=6D456341FS516215S&FEEREFUNDAMT=0%2e50&GROSSREFUNDAMT=10%2e00&NETREFUNDAMT=9%2e50&CURRENCYCODE=USD&TO"
  },
  {
    "path": "spec/fake_response/SetExpressCheckout/failure.txt",
    "chars": 176,
    "preview": "TIMESTAMP=2011%2d02%2d02T02%3a16%3a50Z&CORRELATIONID=379d1b7f97afb&ACK=Failure&L_ERRORCODE0=10001&L_SHORTMESSAGE0=Intern"
  },
  {
    "path": "spec/fake_response/SetExpressCheckout/success.txt",
    "chars": 128,
    "preview": "ACK=Success&BUILD=1721431&CORRELATIONID=5549ea3a78af1&TIMESTAMP=2011-02-02T02%3A02%3A18Z&TOKEN=EC-5YJ90598G69065317&VERS"
  },
  {
    "path": "spec/helpers/fake_response_helper.rb",
    "chars": 753,
    "preview": "require 'fakeweb'\n\nmodule FakeResponseHelper\n\n  def fake_response(file_path, api = :NVP, options = {})\n    endpoint = ca"
  },
  {
    "path": "spec/paypal/exception/api_error_spec.rb",
    "chars": 2373,
    "preview": "require 'spec_helper.rb'\n\ndescribe Paypal::Exception::APIError do\n  let(:error) { Paypal::Exception::APIError.new(params"
  },
  {
    "path": "spec/paypal/exception/http_error_spec.rb",
    "chars": 282,
    "preview": "require 'spec_helper.rb'\n\ndescribe Paypal::Exception::HttpError do\n  subject { Paypal::Exception::HttpError.new(400, 'Ba"
  },
  {
    "path": "spec/paypal/express/request_spec.rb",
    "chars": 19007,
    "preview": "require 'spec_helper.rb'\n\ndescribe Paypal::Express::Request do\n  class Paypal::Express::Request\n    attr_accessor :_sent"
  },
  {
    "path": "spec/paypal/express/response_spec.rb",
    "chars": 2344,
    "preview": "require 'spec_helper.rb'\n\ndescribe Paypal::Express::Response do\n  before { fake_response 'SetExpressCheckout/success' }\n"
  },
  {
    "path": "spec/paypal/ipn_spec.rb",
    "chars": 478,
    "preview": "require 'spec_helper'\n\ndescribe Paypal::IPN do\n  describe '.verify!' do\n    context 'when valid' do\n      before { fake_"
  },
  {
    "path": "spec/paypal/nvp/request_spec.rb",
    "chars": 3163,
    "preview": "require 'spec_helper.rb'\n\ndescribe Paypal::NVP::Request do\n  let :attributes do\n    {\n      :username => 'nov',\n      :p"
  },
  {
    "path": "spec/paypal/nvp/response_spec.rb",
    "chars": 4580,
    "preview": "require 'spec_helper.rb'\n\ndescribe Paypal::NVP::Response do\n  let(:return_url) { 'http://example.com/success' }\n  let(:c"
  },
  {
    "path": "spec/paypal/payment/common/amount_spec.rb",
    "chars": 936,
    "preview": "require 'spec_helper.rb'\n\ndescribe Paypal::Payment::Common::Amount do\n  let :keys do\n    Paypal::Payment::Common::Amount"
  },
  {
    "path": "spec/paypal/payment/recurring/activation_spec.rb",
    "chars": 465,
    "preview": "require 'spec_helper'\n\ndescribe Paypal::Payment::Recurring::Activation do\n  let :instance do\n    Paypal::Payment::Recurr"
  },
  {
    "path": "spec/paypal/payment/recurring_spec.rb",
    "chars": 5010,
    "preview": "require 'spec_helper.rb'\n\ndescribe Paypal::Payment::Recurring do\n  let :keys do\n    Paypal::Payment::Recurring.optional_"
  },
  {
    "path": "spec/paypal/payment/request/item_spec.rb",
    "chars": 731,
    "preview": "require 'spec_helper.rb'\n\ndescribe Paypal::Payment::Request::Item do\n  let :instance do\n    Paypal::Payment::Request::It"
  },
  {
    "path": "spec/paypal/payment/request_spec.rb",
    "chars": 4733,
    "preview": "require 'spec_helper.rb'\n\ndescribe Paypal::Payment::Request do\n  let :instant_request do\n    Paypal::Payment::Request.ne"
  },
  {
    "path": "spec/paypal/payment/response/address_spec.rb",
    "chars": 666,
    "preview": "require 'spec_helper.rb'\n\ndescribe Paypal::Payment::Response::Address do\n  let :keys do\n    Paypal::Payment::Response::A"
  },
  {
    "path": "spec/paypal/payment/response/info_spec.rb",
    "chars": 3150,
    "preview": "require 'spec_helper.rb'\n\ndescribe Paypal::Payment::Response::Info do\n  let :attribute_mapping do\n    Paypal::Payment::R"
  },
  {
    "path": "spec/paypal/payment/response/item_spec.rb",
    "chars": 1221,
    "preview": "require 'spec_helper.rb'\n\ndescribe Paypal::Payment::Response::Info do\n  let :attributes do\n    {\n      :NAME => 'Item Na"
  },
  {
    "path": "spec/paypal/payment/response/payer_spec.rb",
    "chars": 658,
    "preview": "require 'spec_helper.rb'\n\ndescribe Paypal::Payment::Response::Payer do\n  let :keys do\n    Paypal::Payment::Response::Pay"
  },
  {
    "path": "spec/paypal/payment/response_spec.rb",
    "chars": 444,
    "preview": "require 'spec_helper.rb'\n\ndescribe Paypal::Payment::Response do\n  describe '.new' do\n    context 'when non-supported att"
  },
  {
    "path": "spec/paypal/util_spec.rb",
    "chars": 1288,
    "preview": "require 'spec_helper.rb'\n\ndescribe Paypal::Util do\n  describe '.formatted_amount' do\n    it 'should return String in \"xx"
  },
  {
    "path": "spec/spec_helper.rb",
    "chars": 370,
    "preview": "require 'simplecov'\n\nSimpleCov.start do\n  add_filter 'spec'\nend\n\nrequire 'paypal'\nrequire 'rspec'\nrequire 'helpers/fake_"
  }
]

About this extraction

This page contains the full source code of the nov/paypal-express GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 85 files (119.0 KB), approximately 37.4k tokens, and a symbol index with 144 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!