module Paypal # This is a collection of helpers which aid in the creation of paypal buttons # # Example: # # <%= form_tag Paypal::Notification.ipn_url %> # # <%= paypal_setup "Item 500", Money.us_dollar(50000), "bob@bigbusiness.com" %> # Please press here to pay $500US using paypal. <%= submit_tag %> # # <% end_form_tag %> # # For this to work you have to include these methods as helpers in your rails application. # One way is to add "include Paypal::Helpers" in your application_helper.rb # See Paypal::Notification for information on how to catch payment events. module Helpers # Convenience helper. Can replace <%= form_tag Paypal::Notification.ipn_url %> # takes optional url parameter, default is Paypal::Notification.ipn_url def paypal_form_tag(url = Paypal::Notification.ipn_url, options = {}) form_tag(url, options) end # This helper creates the hidden form data which is needed for a paypal purchase. # # * item_number -- The first parameter is the item number. This is for your personal organization and can # be arbitrary. Paypal will sent the item number back with the IPN so its a great place to # store a user ID or a order ID or something like this. # # * amount -- should be a parameter of type Money ( see http://leetsoft.com/api/money ) but can also # be a string of type "50.00" for 50$. If you use the string syntax make sure you set the current # currency as part of the options hash. The default is USD # # * business -- This is your paypal account name ( a email ). This needs to be a valid paypal business account. # # The last parameter is a options hash. You can set or override any Paypal-recognized parameter, including: # # * :cmd -- default is '_xclick'. # * :quantity -- default is '1'. # * :no_note -- default is '1'. # * :item_name -- default is 'Store purchase'. This is the name of the purchase which will be displayed # on the paypal page. # * :no_shipping -- default is '1'. By default we tell paypal that no shipping is required. Usually # the shipping address should be collected in our application, not by paypal. # * :currency -- default is 'USD'. If you provide a Money object, that will automatically override # the value. # * :charset -- default is 'utf-8'. # * :notify_url -- If provided paypal will send its IPN notification once a # purchase is made, canceled or any other status changes occur. # * :return -- If provided paypal will redirect a user back to this url after a # successful purchase. Useful for a kind of thankyou page. # * :cancel_return -- If provided paypal will redirect a user back to this url when # the user cancels the purchase. # * :tax -- the tax for the store purchase. Same format as the amount parameter but optional # * :invoice -- Unique invoice number. User will never see this. optional # * :custom -- Custom field. User will never see this. optional # # Generating encrypted form data # # The helper also supports the generation of encrypted button data. Please see the README for more information # on the setup and prerequisite steps for using encrypted forms. # # The following options must all be provided (as strings) to encrypt the data: # # * :business_key -- The private key you have generated # * :business_cert -- The public certificate you have also uploaded to Paypal # * :business_certid -- The certificate ID that Paypal has assigned to your certificate. # # Examples: # # <%= paypal_setup @order.id, Money.us_dollar(50000), "bob@bigbusiness.com" %> # <%= paypal_setup @order.id, '50.00', "bob@bigbusiness.com", :currency => 'USD' %> # <%= paypal_setup @order.id, '50.00', "bob@bigbusiness.com", :currency => 'USD', :notify_url => url_for(:only_path => false, :action => 'paypal_ipn') %> # <%= paypal_setup @order.id, Money.ca_dollar(50000), "bob@bigbusiness.com", :item_name => 'Snowdevil shop purchase', :return_url => paypal_return_url, :cancel_url => paypal_cancel_url, :notify_url => paypal_ipn_url %> # <%= paypal_setup @order.id, Money.ca_dollar(50000), "bob@bigbusiness.com", :item_name => 'Snowdevil shop purchase', :return_url => paypal_return_url, :cancel_url => paypal_cancel_url, :business_key => @business_key, :business_cert => @business_cert, :business_certid => @business_certid %> # def paypal_setup(item_number, amount, business, options = {}) misses = (options.keys - valid_setup_options) raise ArgumentError, "Unknown option #{misses.inspect}" if not misses.empty? params = { :cmd => "_ext-enter", :redirect_cmd => "_xclick", :quantity => 1, :business => business, :item_number => item_number, :item_name => 'Store purchase', :no_shipping => '1', :no_note => '1', :charset => 'utf-8' }.merge(options) params[:currency_code] = amount.currency if amount.respond_to?(:currency) params[:currency_code] = params.delete(:currency) if params[:currency] params[:currency_code] ||= 'USD' # We accept both strings and money objects as amount amount = amount.cents.to_f / 100.0 if amount.respond_to?(:cents) params[:amount] = sprintf("%.2f", amount) # same for tax tax = params[:tax] if tax tax = tax.cents.to_f / 100.0 if tax.respond_to?(:cents) params[:tax] = sprintf("%.2f", tax) end # look for encryption parameters, save them outsite the parameter hash. business_key = params.delete(:business_key) business_cert = params.delete(:business_cert) business_certid = params.delete(:business_certid) # Build the form returning button = [] do # Only attempt an encrypted form if we have all the required fields. if business_key and business_cert and business_certid require 'openssl' # Convert the key and certificates into OpenSSL-friendly objects. paypal_cert = OpenSSL::X509::Certificate.new(Paypal::Notification.paypal_cert) business_key = OpenSSL::PKey::RSA.new(business_key) business_cert = OpenSSL::X509::Certificate.new(business_cert) # Put the certificate ID back into the parameter hash the way Paypal wants it. params[:cert_id] = business_certid # Prepare a string of data for encryption data = "" params.each_pair {|k,v| data << "#{k}=#{v}\n"} # Sign the data with our key/certificate pair signed = OpenSSL::PKCS7::sign(business_cert, business_key, data, [], OpenSSL::PKCS7::BINARY) # Encrypt the signed data with Paypal's public certificate. encrypted = OpenSSL::PKCS7::encrypt([paypal_cert], signed.to_der, OpenSSL::Cipher::Cipher::new("DES3"), OpenSSL::PKCS7::BINARY) # The command for encrypted forms is always '_s-xclick'; the real command is in the encrypted data. button << tag(:input, :type => 'hidden', :name => 'cmd', :value => "_s-xclick") button << tag(:input, :type => 'hidden', :name => 'encrypted', :value => encrypted) else # Just emit all the parameters that we have as hidden fields. # Note that the sorting isn't really needed, but it makes testing a lot easier for now. params.each do |key, value| button << tag(:input, :type => 'hidden', :name => key, :value => value) end end end.join("\n") end # Pass an address to paypal so that all singup forms can be prefilled # # * email -- Customer's email address # * first_name -- Customer's first name. Must be alpha-numeric, with a 32 character limit # * last_name -- Customer's last name. Must be alpha-numeric, with a 64 character limit # * address1 -- First line of customer's address. Must be alpha-numeric, with a 100 character limit # * address2 -- Second line of customer's address. Must be alpha-numeric, with a 100 character limit # * city -- City of customer's address. Must be alpha-numeric, with a 100 character limit # * state -- State of customer's address. Must be official 2 letter abbreviation # * zip -- Zip code of customer's address # * night_phone_a -- Area code of customer's night telephone number # * night_phone_b -- First three digits of customer's night telephone number # * day_phone_a -- Area code of customer's daytime telephone number # * day_phone_b -- First three digits of customer's daytime telephon def paypal_address(options = {}) options.collect do |key, value| tag(:input, :type => 'hidden', :name => key, :value => value) end.join("\n") end private # See https://www.paypal.com/IntegrationCenter/ic_std-variable-reference.html for details on the following options. def valid_setup_options [ # Generic Options :cmd, # IPN Support :notify_url, # Item Information :item_name, :quantity, :undefined_quantity, :on0, :os0, :on1, :os1, # Display Information :add, :cancel_return, :cbt, :cn, :cpp_header_image, :cpp_headerback_color, :cpp_headerborder_color, :cpp_payflow_color, :cs, :display, :image_url, :no_note, :no_shipping, :page_style, :return, :rm, # Transaction Information :address_override, :currency, :currency_code, :custom, :handling, :invoice, :redirect_cmd, :shipping, :tax, :tax_cart, # Shopping Cart Options :amount, :business, :handling_cart, :paymentaction, :rupload, :charset, :upload, # Prepopulating PayPal FORMs or Address Overriding :address1, :address2, :city, :country, :email, :first_name, :last_name, :lc, :night_phone_a, :night_phone_b, :night_phone_c, :state, :zip, # Prepopulating Business Account Sign-up :business_address1, :business_address2, :business_city, :business_state, :business_country, :business_cs_email, :business_cs_phone_a, :business_cs_phone_b, :business_cs_phone_c, :business_url, :business_night_phone_a, :business_night_phone_b, :business_night_phone_c, # End of list from https://www.paypal.com/IntegrationCenter/ic_std-variable-reference.html # The following items are known to exist but are not yet on the above page. :business_zip, :day_phone_a, :day_phone_b, :day_phone_c, # Subscription Options :a1, # Trial Amount 1 :p1, # Trial Period 1 :t1, # Trial Period 1 Units (D=days, W=weeks, M=months, Y=years) :a2, # Trial Amount 2 :p2, # Trial Period 2 :t2, # Trial Period 2 Units :a3, # Regular Subscription Amount :p3, # Regular Subscription Period :t3, # Regular Subscription Period Units :src, # Recurring Payments? (1=yes, default=0) :sra, # Reattempt Transaction on Failure? (1=yes, default=0) :srt, # Recurring Times (number of renewals before auto-cancel, default=forever) :usr_manage, # Username and Password Generator? (1=yes, default=0) :modify, # Modification Behaviour (0=new subs only, 1=new or modify, 2=modify existing only, default=0) # Encryption Options - used internally only. :business_key, # Your private key :business_cert, # Your public certificate :business_certid # Your public certificate ID (from Paypal) ] end end end