def parse_priv_key(privstr): if not isinstance(privstr, str): raise TypeError('str argument required') if not privstr.startswith('-----BEGIN DSA PRIVATE KEY-----') \ and not privstr.startswith('-----BEGIN RSA PRIVATE KEY-----'): raise ValueError("public key didn't start with '-----BEGIN ANY PRIVATE KEY-----'") mem = BIO.MemoryBuffer(privstr) return DSA.load_key_bio(mem)
def payment_pta(self): '''Generates the Payment Transaction Authorization, or PTA. It does not communicate with the server.''' #------------------------------------ # PTA Payment Block #------------------------------------ p1 = pcos.create_output_block( 'P1' ) # member authentication token mat = self.args['mat'] if len( mat ) != 40: raise RuntimeError("MAT must be 40-characters long" % self.cmd) p1.write_varstr( binascii.unhexlify( mat ) ) # cert. create time and expiry now = long( time.time() + 0.5 ) p1.write_ulong( now ) # certificate create-time p1.write_ulong( now + 24 * 3600 ) # certificate expires in 24 hrs # payment-limit (payment_int, payment_scale) = decimal_to_parts(Decimal(self.args['limit'])) p1.write_long( payment_int ) # value p1.write_int( payment_scale ) # scale # currency p1.write_fixstr( "USD", size=3 ) # recipient p1.write_varstr( "" ) # ref-data p1.write_varstr( "" ) # note p1.write_varstr( "" ) print (" %s bytes => Payment Block" % p1.size()) #------------------------------------ # PTA Signature Block #------------------------------------ s1 = pcos.create_output_block( 'S1' ) # checksum Payment Block digest = hashlib.sha1(p1.as_bytearray()).digest() # sign the checksum dsa_priv_key = BIO.MemoryBuffer( TEST_DSA_KEY_PRV_PEM ) signer = DSA.load_key_bio( dsa_priv_key ) signature = signer.sign_asn1( digest ) # store the signature of the Payment Block s1.write_varstr( signature ) print (" %s bytes => Signature Block" % s1.size()) #------------------------------------ # PTA Message # * Payment Block # * Signature Block #------------------------------------ pta = pcos.Doc( name="Pa" ) pta.add( p1 ) pta.add( s1 ) #------------------------------------ # A-PTA Private (PTA) Block #------------------------------------ a1 = pcos.create_output_block( 'A1' ) # encrypt the PTA message txn_pub_key = BIO.MemoryBuffer( API_TRANSACTION_KEY_PEM ) encrypter = RSA.load_pub_key_bio( txn_pub_key ) # RSA Encryption Scheme w/ Optimal Asymmetric Encryption Padding input_data = pta.as_bytearray() print (" %s bytes => header+meta" % (len(input_data) - s1.size() - p1.size())) print ("-------------\n %s bytes => Total PTA\n---------" % len(input_data)) encrypted = encrypter.public_encrypt( pta.as_bytearray(), RSA.pkcs1_padding ) a1.write_fixstr( encrypted, size=len(encrypted) ) #------------------------------------ # A-PTA Public Block #------------------------------------ k1 = pcos.create_output_block( 'K1' ) # encryption key identifier k1.write_fixstr( API_TRANSACTION_KEY_ID, size=len(API_TRANSACTION_KEY_ID) ) #------------------------------------ # A-PTA Message #------------------------------------ apta = pcos.Doc( name="Ap" ) apta.add( a1 ) apta.add( k1 ) apta_bytes = apta.as_bytearray() # write serialized data as binary and qr-code payload_file = open('apta.pcos', 'w') payload_file.write( apta_bytes ) payload_file.close() print ("Saved APTA object to 'apta.pcos'") # optionally generate qr-code try: import qrcode qr = qrcode.QRCode(version=None, box_size=3, error_correction=qrcode.constants.ERROR_CORRECT_L) qr.add_data( apta_bytes ) qr.make(fit=True) img = qr.make_image() img.save('apta.png') print ("APTA-QR: apta.png, version %s" % (qr.version)) except ImportError: log.warn("QR-Code not written -- qrcode module not found") return apta_bytes
def charge(self, payment_type, payment_bytes): #------------------------------------ # Payment Block #------------------------------------ payment_block = pcos.create_output_block( payment_type ) # we use "fixstr" becase we don't want size-prefix payment_block.write_fixstr(payment_bytes, len(payment_bytes)) #------------------------------------ # Payment Request Block #------------------------------------ paymnt_req_block = pcos.create_output_block( 'R1' ) # mat paymnt_req_block.write_varstr( binascii.unhexlify( self.args['receiver_mat'] ) ) # ref data paymnt_req_block.write_varstr( '' ) # create-time paymnt_req_block.write_ulong( long( time.time() + 0.5 ) ) (charge_int, charge_scale) = decimal_to_parts(Decimal(self.args['charge'])) paymnt_req_block.write_long( charge_int ) # value paymnt_req_block.write_int( charge_scale ) # scale # tax paymnt_req_block.write_bool(True) # optional indicator (tax_value, tax_scale) = decimal_to_parts(Decimal(self.args['tax'])) paymnt_req_block.write_long( tax_value ) # value paymnt_req_block.write_int( tax_scale ) # scale # tip paymnt_req_block.write_bool(True) # optional indicator (tip_value, tip_scale) = decimal_to_parts(Decimal(self.args['tip'])) paymnt_req_block.write_long( tip_value ) # value paymnt_req_block.write_int( tip_scale ) # scale # currency paymnt_req_block.write_fixstr( "USD", size=3 ) # invoice ID paymnt_req_block.write_varstr( 'inv-123' ) # note paymnt_req_block.write_varstr( 'happy meal' ) # no geo-location available paymnt_req_block.write_bool(False) # list of purchased goods paymnt_req_block.write_int(0) #------------------------------------ # Payment Request Signature Block #------------------------------------ paymnt_req_signature_block = pcos.create_output_block( 'S1' ) # checksum Payment Request Block digest = hashlib.sha1(paymnt_req_block.as_bytearray()).digest() # sign the checksum dsa_priv_key = BIO.MemoryBuffer( TEST_DSA_KEY_PRV_PEM ) signer = DSA.load_key_bio( dsa_priv_key ) signature = signer.sign_asn1( digest ) # store the signature of the Payment Block paymnt_req_signature_block.write_varstr( signature ) #------------------------------------ # Payment Request Message #------------------------------------ req = pcos.Doc( name="Pt" ) req.add( paymnt_req_block ) req.add( paymnt_req_signature_block ) req.add( payment_block ) res = self.send( req ) # there can be two types of results: Ap or Np, depending # success or failure due to insufficient funds if res.message_id != 'Ap' and res.message_id != 'Np': raise RuntimeError("Unexpected payment result '%s'" % res.message_id) # read balance balance = res.block( 'Bo' ) ref_data = balance.read_varstr() # ref_data transaction_id = balance.read_varstr() # tx-id if res.message_id == 'Np': code = er.read_uint() what = er.read_varstr() # exact amount given? if balance.read_bool(): exact_balance = 'is' else: exact_balance = 'is greater than' # amount value = balance.read_long() # value scale = balance.read_int() # scale balance_amount = value * math.pow(10, scale) # time of last update tm_epoch = balance.read_ulong(); balance_asofdate = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(tm_epoch)) if res.message_id == 'Np': log.warn('%s (tx_id: %s). Balance %s $%s as of %s', what, base64.b32encode(transaction_id), exact_balance, balance_amount, balance_asofdate) else: log.info('Success (tx_id: %s). Balance %s $%s as of %s', base64.b32encode(transaction_id), exact_balance, balance_amount, balance_asofdate)
def payment(self): '''This command generates the Payment Transaction Authorization, or PTA. It does not communicate with the server, only produces a file.''' #------------------ # PTA public-block #------------------ p1 = pcos.Block( 'P1', 512, 'O' ) now = long( time.time() + 0.5 ) p1.write_int64( now ) # certificate create-time p1.write_int64( now + 24 * 3600 ) # certificate expiry (in 24 hrs) # p1.write_int64( now + 60*2 ) # certificate expiry # payment-limit (payment_int, payment_scale) = decimal_to_parts(Decimal(self.args['limit'])) p1.write_int64( payment_int ) # value p1.write_int16( payment_scale ) # scale # gratuity tip = tip_type = None tipv = self.args.get('tip_pct', None) if tipv: tip_type = 'P' else: tipv = self.args.get('tip_abs', None) if tipv: tip_type = 'A' if tipv: (tip_int, tip_scale) = decimal_to_parts(Decimal(tipv)) p1.write_byte(1) # optional indicator p1.write_fixed_string(tip_type, size=1) # tip type (P or A) p1.write_int64( tip_int ) # value p1.write_int16( tip_scale ) # scale else: p1.write_byte(0) # optional indicator -- no tip p1.write_fixed_string( "USD", size=3 ) # currency p1.write_fixed_string( binascii.unhexlify( API_TRANSACTION_KEY_ID ), size=4 ) # key-ID p1.write_short_string( '', max=127 ) # receiver p1.write_short_string( '', max=127 ) # note #------------------- # PTA private-block #------------------- priv = pcos.Block( 'S1', 512, 'O' ) # member authentication token mat = self.args['mat'] if len( mat ) != 40: raise RuntimeError("MAT must be 40-characters long" % self.cmd) priv.write_fixed_string( binascii.unhexlify( self.args['mat'] ), size=20 ) # mat priv.write_short_string( '', max=20 ) # ref-data # sign the public-block # * first, produce the checksum digest = hashlib.sha1(str(p1)).digest() # * then sign the checksum dsa_priv_key = BIO.MemoryBuffer( TEST_DSA_KEY_PRV_PEM ) signer = DSA.load_key_bio( dsa_priv_key ) signature = signer.sign_asn1( digest ) priv.write_short_string( signature, max=48 ) # signature of the pub block priv.write_short_string( '', max=20 ) # empty user data # encrypt the private-block txn_pub_key = BIO.MemoryBuffer( API_TRANSACTION_KEY_PEM ) encrypter = RSA.load_pub_key_bio( txn_pub_key ) # RSA Encryption Scheme w/ Optimal Asymmetric Encryption Padding encrypted = encrypter.public_encrypt( str(priv), RSA.pkcs1_oaep_padding ) # At this point we no longer need the private object. We only attach the # encrypted instance. s1 = pcos.Block( 'S1', 512, 'O' ) s1.write_fixed_string( encrypted, size=128 ) #------------------- # PTA envelope #------------------- env = pcos.Doc( name="Pa" ) # order in which we add blocks doesn't matter env.add( p1 ) env.add( s1 ) # write serialized data as binary and qr-code encoded = env.encoded() reqf = open('pta.pcos', 'w') reqf.write( encoded ) reqf.close() print ("Saved PTA object to 'pta.pcos'") # optionally generate qr-code try: import qrcode qr = qrcode.QRCode(version=None, box_size=3, error_correction=qrcode.constants.ERROR_CORRECT_L) qr.add_data( encoded ) qr.make(fit=True) img = qr.make_image() img.save('pta.png') print ("PTA-QR: pta.png, version %s" % (qr.version)) except ImportError: log.warn("QR-Code not written -- qrcode module not found") return encoded