Payment Gateway Integration

Hi,

I’m trying to prepare a payment gateway. The payment provider also wants product information in the basket. I didn’t find out how to call the product list in the basket. The code I’m working on is as follows.

def log(*args, **kwargs):
	print("\n".join(args))

class IyzicoSettings(Document):
	service_name = "Iyzico"
	supported_currencies = ["TRY", "USD", "EUR", "GBP"]
	is_embedable = True

	def validate(self):
		create_payment_gateway("Iyzico")
		call_hook_method("payment_gateway_enabled", gateway=self.service_name)
		if not self.flags.ignore_mandatory:
			self.validate_iyzico_credentails()

	def on_update(self):
		pass

	def get_embed_context(self, context):
		# list countries for billing address form
		context["iyzico_countries"] = frappe.get_list("Country", fields=["country_name", "name"], ignore_permissions=1)
		default_country = frappe.get_value("System Settings", "System Settings", "country")
		default_country_doc = next((x for x in context["iyzico_countries"] if x.name == default_country), None)

		country_idx = context["iyzico_countries"].index(default_country_doc)
		context["iyzico_countries"].pop(country_idx)
		context["iyzico_countries"] = [default_country_doc] + context["iyzico_countries"]

		context["year"] = datetime.today().year

		# get the iyzico user record
		#iyzico_user = get_iyzico_user()

		#if iyzico_user:
		#	context["stored_payments"] = iyzico_user.get("stored_payments", [])

	def get_embed_form(self, context={}):

		context.update({
			"source": "templates/includes/integrations/iyzico/embed.html"
		})
		context = _dict(context)

		self.get_embed_context(context)

		return {
			"form": frappe.render_template(context.source, context),
			"style_url": "/assets/css/iyzico_embed.css",
			"script_url": "/assets/js/iyzico_embed.js"
		}

	def validate_iyzico_credentails(self):
		pass

	def validate_transaction_currency(self, currency):
		if currency not in self.supported_currencies:
			frappe.throw(_("Please select another payment method. {0} does not support transactions in currency \"{1}\"").format(self.service_name, currency))

	def build_iyzico_request(self, **kwargs):
		"""Creates an iyzico Request record to keep params off the url"""

		data = {
			"doctype": "Iyzico Request",
			"status": "Issued",
		}
		data.update(kwargs)
		del data["reference_docname"] # have to set it after insert

		request = frappe.get_doc(data)
		request.flags.ignore_permissions = 1
		request.insert()

		# TODO: Why must we save doctype first before setting docname?
		request.reference_docname = kwargs["reference_docname"]
		request.save()
		frappe.db.commit()

		return request

	def get_payment_url(self, **kwargs):
		request = self.build_iyzico_request(**kwargs)
		url = "./integrations/iyzico_checkout/{0}"
		result = get_url(url.format(request.get("name" )))
		return result

	def get_settings(self):
		settings = frappe._dict({
			"api_key": self.api_key,
			"secret_key": self.get_password(fieldname="secret_key", raise_exception=False)
		})

		return settings

	def process_payment(self):
		# used for feedback about which payment was used
		iyzico_data = {}
		# the current logged in contact
		contact = get_contact()
		# get iyzico user if available
		#iyzico_user = get_iyzico_user()
		# the cc data available
		data = self.process_data

		# get auth keys
		settings = self.get_settings()
		# fetch redirect info
		redirect_to = data.get("notes", {}).get("redirect_to") or None
		redirect_message = data.get("notes", {}).get("redirect_message") or None

		# uses dummy request doc for unittests as we are only testing processing
		if not data.get("unittest"):
			if data.get("name"):
				request = frappe.get_doc("Iyzico Request", data.get("name"))
			else:
				# Create request from scratch when embeding form on the fly
				#
				# This allows payment processing without having to pre-create
				# a request first.
				#
				# This path expects all the payment request information to be
				# available!!
				#
				# keys expected: ('amount', 'currency', 'order_id', 'title', \
				#                 'description', 'payer_email', 'payer_name', \
				#                 'reference_docname', 'reference_doctype')
				request = self.build_iyzico_request(**{ \
					key: data[key] for key in \
						('amount', 'currency', 'order_id', 'title', \
						 'description', 'payer_email', 'payer_name', \
						 'reference_docname', 'reference_doctype') })

				data["name"] = request.get("name")
		else:
			request = frappe.get_doc({"doctype": "Iyzico Request"})

		request.flags.ignore_permissions = 1

		# set the max log level as per settings
		request.max_log_level(self.log_level)

		try:

			if self.card_info:
				# ensure card fields exist
				required_card_fields = ['cardHolderName', 'cardNumber', 'expireMonth', 'expireYear', 'cvc']
				for f in required_card_fields:
					if not self.card_info.get(f):
						request.status = "Error"
						return request,	None, "Missing field: %s" % f, {}

			# prepare authorize api
			#authorize.Configuration.configure(
			#	authorize.Environment.TEST if self.use_sandbox else authorize.Environment.PRODUCTION,
			#	settings.api_login_id,
			#	settings.api_transaction_key
			#)

			ayar = {
			    'api_key': settings.api_key,
				'secret_key': settings.secret_key,
				'base_url': iyzipay.base_url
			}

			# cache billing fields as per authorize api requirements
			buyer = iyzico_buyer_address(self.billing_info)
			billingAddress = iyzico_address(self.billing_info)
			if self.shipping_info:
				shippingAddress = iyzico_address(self.billing_info)
			else:
				shippingAddress = None

			# attempt to find valid email address
			email = self.process_data.get("payer_email")

			if email:
				email = email.split(',')[0]

			if "@" not in email and contact:
				email = contact.get("email_id")

				if "@" not in email:
					if contact and contact.user:
						email = frappe.get_value("User", contact.user, "email_id")

						if "@" not in email:
							log("Iyzico FAILURE! Bad email: {0}".format(email))
							raise ValueError("There are no valid emails associated with this customer")


			address = {
				'contactName': 'Jane Doe',
				'city': 'Istanbul',
				'country': 'Turkey',
				'address': 'Nidakule Göztepe, Merdivenköy Mah. Bora Sok. No:1',
				'zipCode': '34732'
			}

			buyer = {
				'id': 'BY789',
			    'name': 'John',
			    'surname': 'Doe',
			    'gsmNumber': '+905350000000',
			    'email': 'email@email.com',
			    'identityNumber': '74300864791',
			    'lastLoginDate': '2015-10-05 12:43:35',
			    'registrationDate': '2013-04-21 15:12:09',
			    'registrationAddress': 'Nidakule Göztepe, Merdivenköy Mah. Bora Sok. No:1',
			    'ip': '85.34.78.112',
			    'city': 'Istanbul',
			    'country': 'Turkey',
			}


			# build transaction data
			transaction_data = {
				"locale": "TR",
				"conversationId": "123456789",
				"price": flt(self.process_data.get("amount")),
				"paidPrice": flt(self.process_data.get("amount")),
				"currency": "TRY",
				"installment": 1,
				"paymentGroup": "OTHER",
				'buyer': buyer,
				'shippingAddress': address,
				'billingAddress': address
			}

			# track ip for tranasction records
			if frappe.local.request_ip:
				transaction_data.update({
					"buyer": {
						"ip": frappe.local.request_ip
					}
				})

			# get iyzico profile informatio for stored payments
			iyzico_profile = self.process_data.get("iyzico_profile");

			# use card
			# see: https://vcatalano.github.io/py-authorize/transaction.html
			if self.card_info != None:
				# exp formating for sale/auth api
				transaction_data.update({
					"paymentCard": {
						"cardHolderName": self.card_info.get("cardHolderName"),
						"cardNumber": self.card_info.get("cardNumber"),
						"expireMonth": self.card_info.get("expireMonth"),
						"expireYear": self.card_info.get("expireYear"),
						"cvc": self.card_info.get("cvc")
					}
				})
			elif iyzico_profile:

				# if the customer_id isn't provided, then fetch from iyzicouser
				if not iyzico_profile.get("customer_id"):
					iyzico_profile["customer_id"] = iyzico_user.get("iyzico_id")

				# or stored payment
				transaction_data.update({
					"customer_id": iyzico_profile.get("customer_id"),
					"payment_id": iyzico_profile.get("payment_id")
				})

				# track transaction payment profile ids to return later
				iyzico_data.update({
					"customer_id": iyzico_profile.get("customer_id"),
					"payment_id": iyzico_profile.get("payment_id")
				})
			else:
				raise "Missing Credit Card Information"


			# add billing information if available
			contactName = self.card_info["cardHolderName"]

			if len(billingAddress.keys()):
				transaction_data["billingAddress"] = billingAddress
				transaction_data["billingAddress"]["contactName"] = name

			if shippingAddress and len(shippingAddress.keys()):
				transaction_data["shippingAddress"] = billingAddress
				transaction_data["shippingAddress"]["contactName"] = name

			if buyer and len(buyer.keys()):
				transaction_data["buyer"] = billingAddress
				transaction_data["buyer"]["email"] = email
				transaction_data["identityNumber"] = "74300864791"

			# include line items if available
		> if self.process_data.get("items"):
				transaction_data["basketItems"] = self.process_data.get("items")
			request.log_action("Requesting Transaction: %s" % \
				json.dumps(transaction_data), "Debug")

			# performt transaction finally
			result = iyzipay.Payment().create(transaction_data, ayar)
			print(result.read())
			request.log_action(result, "Debug")

			# if all went well, record transaction id
			request.status = "Authorized"
			request.flags.ignore_permissions = 1

I need to add the product information in the basket to basketItems.

For example:

basket_items = [
    {
        'id': 'BI101',
        'name': 'Binocular',
        'category1': 'Collectibles',
        'category2': 'Accessories',
        'itemType': 'PHYSICAL',
        'price': '0.3'
    },
    {
        'id': 'BI102',
        'name': 'Game code',
        'category1': 'Game',
        'category2': 'Online Game Items',
        'itemType': 'VIRTUAL',
        'price': '0.5'
    },
    {
        'id': 'BI103',
        'name': 'Usb',
        'category1': 'Electronics',
        'category2': 'Usb / Cable',
        'itemType': 'PHYSICAL',
        'price': '0.2'
    }
]
2 Likes

Hi bro,
did you done that payment gateway integration.

how to do that