Skip to content

Enhanced and organized. #20

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
63 changes: 36 additions & 27 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,17 @@ plugins {
val ktorVersion = "1.6.3"
val libraryVersion = "1.0.1"


android {
namespace = "com.example.zarinpal"
compileSdk = 34

publishing {
singleVariant("release") {
withSourcesJar()
}
}

defaultConfig {
aarMetadata {
minCompileSdk = 24
}
minSdk = 24
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
aarMetadata {
minCompileSdk = 24
}
}

buildTypes {
Expand All @@ -47,6 +40,12 @@ android {
kotlinOptions {
jvmTarget = "1.8"
}

publishing {
singleVariant("release") {
withSourcesJar()
}
}
}

tasks.register<Jar>("javadocJar") {
Expand All @@ -64,20 +63,24 @@ publishing {
afterEvaluate {
from(components["release"])
}

artifact(tasks["javadocJar"]) {
classifier = "javadoc"
}

pom {
dependencies {
implementation("io.ktor:ktor-client-core:$ktorVersion")
implementation("io.ktor:ktor-client-android:$ktorVersion")
implementation("io.ktor:ktor-client-serialization:$ktorVersion")
implementation("io.ktor:ktor-client-logging:$ktorVersion")
implementation("ch.qos.logback:logback-classic:1.2.3")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.0")
listOf(
"io.ktor:ktor-client-core:$ktorVersion",
"io.ktor:ktor-client-android:$ktorVersion",
"io.ktor:ktor-client-serialization:$ktorVersion",
"io.ktor:ktor-client-logging:$ktorVersion",
"ch.qos.logback:logback-classic:1.2.3",
"org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.0"
).forEach {
implementation(it)
}
}

}
}
}
Expand All @@ -86,23 +89,29 @@ publishing {
maven {
url = uri("https://maven.pkg.github.com/alirezabashi98/zarinpal-sdk")
credentials {
credentials.username = System.getenv("GITHUB_USER")
credentials.password = System.getenv("GITHUB_TOKEN")
username = System.getenv("GITHUB_USER")
password = System.getenv("GITHUB_TOKEN")
}
}
}
}

dependencies {
implementation("io.ktor:ktor-client-core:$ktorVersion")
implementation("io.ktor:ktor-client-android:$ktorVersion")
implementation("io.ktor:ktor-client-serialization:$ktorVersion")
implementation("io.ktor:ktor-client-logging:$ktorVersion")
implementation("ch.qos.logback:logback-classic:1.2.3")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.0")
implementation("androidx.core:core-ktx:1.13.0")
listOf(
"io.ktor:ktor-client-core:$ktorVersion",
"io.ktor:ktor-client-android:$ktorVersion",
"io.ktor:ktor-client-serialization:$ktorVersion",
"io.ktor:ktor-client-logging:$ktorVersion",
"ch.qos.logback:logback-classic:1.2.3",
"org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.0",
"androidx.core:core-ktx:1.13.0"
).forEach {
implementation(it)
}
}

tasks.register<Copy>("copyLibs") {
from(configurations.getByName("implementation"))
into("libs")
}
}

120 changes: 40 additions & 80 deletions src/main/java/com/example/zarinpal/ZarinPal.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,127 +23,87 @@ import com.example.zarinpal.utils.Validator
* ZarinPal class handles all interactions with the ZarinPal API.
* Provides methods for creating payments, verifying payments, refunds, reversals, and more.
*/
class ZarinPal(config: Config) {
class ZarinPal(private val config: Config) {

// Instance of PaymentService used for API calls
private val service = PaymentService.create(config)

// Configuration object containing sandbox mode and other settings
private val config = config

/**
* Creates a new payment and returns the response.
* @param paymentRequest Request data for creating the payment.
* @param redirectUrl Callback function to handle the payment gateway URL and status.
* @return [CreatePaymentDataResponse] containing details of the created payment.
*/
suspend fun createPayment(
paymentRequest: CreatePaymentRequest,
redirectUrl: (paymentGatewayUri: String, status: Int) -> Unit
): CreatePaymentDataResponse? {
Validator.validateMerchantId(paymentRequest.merchantId ?: config.merchantId)
Validator.validateCallbackUrl(paymentRequest.callbackUrl)
Validator.validateAmount(paymentRequest.amount)
Validator.validateMobile(paymentRequest.metadata?.mobile)
Validator.validateEmail(paymentRequest.metadata?.email)
Validator.validateCardPan(paymentRequest.cardPan)
with(Validator) {
validateMerchantId(paymentRequest.merchantId ?: config.merchantId)
validateCallbackUrl(paymentRequest.callbackUrl)
validateAmount(paymentRequest.amount)
validateMobile(paymentRequest.metadata?.mobile)
validateEmail(paymentRequest.metadata?.email)
validateCardPan(paymentRequest.cardPan)
}

val paymentResponse =
service.createPayment(paymentRequest)
val paymentResponse = service.createPayment(paymentRequest)

val paymentGatewayUri = HttpRoutes.getRedirectUrl(
sandBox = paymentRequest.sandBox ?: config.sandBox,
authority = paymentResponse?.authority ?: ""
authority = paymentResponse?.authority.orEmpty()
)

redirectUrl(paymentGatewayUri, paymentResponse?.code ?: 0)

return paymentResponse
}

/**
* Verifies a payment using the authority code.
* @param paymentVerifyRequest Request data for verifying the payment.
* @return [PaymentVerificationDataResponse] containing verification details.
*/
suspend fun paymentVerify(
paymentVerifyRequest: PaymentVerifyRequest
): PaymentVerificationDataResponse? {
Validator.validateMerchantId(paymentVerifyRequest.merchantId ?: config.merchantId)
Validator.validateAuthority(paymentVerifyRequest.authority)
Validator.validateAmount(paymentVerifyRequest.amount)
suspend fun paymentVerify(paymentVerifyRequest: PaymentVerifyRequest): PaymentVerificationDataResponse? {
with(Validator) {
validateMerchantId(paymentVerifyRequest.merchantId ?: config.merchantId)
validateAuthority(paymentVerifyRequest.authority)
validateAmount(paymentVerifyRequest.amount)
}

return service.paymentVerify(paymentVerifyRequest)
}

/**
* Inquires about a payment status.
* @param paymentInquiryRequest Request data for the payment inquiry.
* @return [PaymentInquiryDataResponse] containing inquiry details.
*/
suspend fun paymentInquiry(
paymentInquiryRequest: PaymentInquiryRequest
): PaymentInquiryDataResponse? {
Validator.validateMerchantId(paymentInquiryRequest.merchantId ?: config.merchantId)
Validator.validateAuthority(paymentInquiryRequest.authority)
suspend fun paymentInquiry(paymentInquiryRequest: PaymentInquiryRequest): PaymentInquiryDataResponse? {
with(Validator) {
validateMerchantId(paymentInquiryRequest.merchantId ?: config.merchantId)
validateAuthority(paymentInquiryRequest.authority)
}

return service.paymentInquiry(paymentInquiryRequest)
}


/**
* Retrieves unverified payments.
* @param paymentUnVerifiedRequest Optional request data for retrieving unverified payments.
* @return [PaymentUnVerifiedDataResponse] containing unverified payment details.
*/
suspend fun paymentUnVerified(
paymentUnVerifiedRequest: PaymentUnVerifiedRequest = PaymentUnVerifiedRequest()
): PaymentUnVerifiedDataResponse? {
Validator.validateMerchantId(paymentUnVerifiedRequest.merchantId ?: config.merchantId)

return service.paymentUnVerified(paymentUnVerifiedRequest)
}

/**
* Retrieves unverified payments.
* @param paymentUnVerifiedRequest Optional request data for retrieving unverified payments.
* @return [PaymentUnVerifiedDataResponse] containing unverified payment details.
*/
suspend fun paymentReverse(
paymentReverseRequest: PaymentReverseRequest
): PaymentReverseDataResponse? {
Validator.validateMerchantId(paymentReverseRequest.merchantId ?: config.merchantId)
Validator.validateAuthority(paymentReverseRequest.authority)
suspend fun paymentReverse(paymentReverseRequest: PaymentReverseRequest): PaymentReverseDataResponse? {
with(Validator) {
validateMerchantId(paymentReverseRequest.merchantId ?: config.merchantId)
validateAuthority(paymentReverseRequest.authority)
}

return service.paymentReverse(paymentReverseRequest)
}

/**
* Retrieves transaction history.
* @param transactionRequest Request data for fetching transactions.
* @return A list of [Session] objects containing transaction details.
*/
suspend fun getTransactions(
transactionRequest: TransactionRequest
): List<Session>? {
Validator.validateTerminalId(transactionRequest.terminalId)
Validator.validateLimit(transactionRequest.limit)
Validator.validateOffset(transactionRequest.offset)
suspend fun getTransactions(transactionRequest: TransactionRequest): List<Session>? {
with(Validator) {
validateTerminalId(transactionRequest.terminalId)
validateLimit(transactionRequest.limit)
validateOffset(transactionRequest.offset)
}

return service.getTransactions(transactionRequest)
}

/**
* Processes a payment refund.
* @param paymentRefundRequest Request data for refunding the payment.
* @return [PaymentRefundResponse] containing refund details.
*/
suspend fun paymentRefund(
paymentRefundRequest: PaymentRefundRequest
): PaymentRefundResponse? {
Validator.validateSessionId(paymentRefundRequest.sessionId)
Validator.validateAmount(paymentRefundRequest.amount, minAmount = 20_000)
suspend fun paymentRefund(paymentRefundRequest: PaymentRefundRequest): PaymentRefundResponse? {
with(Validator) {
validateSessionId(paymentRefundRequest.sessionId)
validateAmount(paymentRefundRequest.amount, minAmount = 20_000)
}

return service.paymentRefund(paymentRefundRequest)
}
}
}

88 changes: 19 additions & 69 deletions src/main/java/com/example/zarinpal/data/remote/HttpRoutes.kt
Original file line number Diff line number Diff line change
@@ -1,19 +1,10 @@
package com.example.zarinpal.data.remote

/**
* This object contains constants and functions related to the HTTP routes for interacting with the ZarinPal payment gateway.
*
* @property BASE_URL The base URL for the live ZarinPal payment gateway.
* @property BASE_URL_SANDBOX The base URL for the sandbox (test) environment of the ZarinPal payment gateway.
* @property START_PAY_URL The URL endpoint for starting a payment.
* @property BASE_URL_GRAPH The base URL for the GraphQL API of ZarinPal.
* @property PAYMENT The endpoint for creating a payment request.
* @property PAYMENT_VERIFY The endpoint for verifying a payment.
* @property PAYMENT_INQUIRY The endpoint for inquiring about a payment.
* @property PAYMENT_UN_VERIFIED The endpoint for querying unverified payments.
* @property PAYMENT_REVERSE The endpoint for reversing a payment.
* Utility object for building HTTP routes related to the ZarinPal payment gateway.
*/
object HttpRoutes {

private const val BASE_URL = "https://payment.zarinpal.com"
private const val BASE_URL_SANDBOX = "https://sandbox.zarinpal.com"
private const val START_PAY_URL = "/pg/StartPay/"
Expand All @@ -23,68 +14,27 @@ object HttpRoutes {
private const val PAYMENT = "/pg/v4/payment/request.json"
private const val PAYMENT_VERIFY = "/pg/v4/payment/verify.json"
private const val PAYMENT_INQUIRY = "/pg/v4/payment/inquiry.json"
private const val PAYMENT_UN_VERIFIED = "/pg/v4/payment/unVerified.json"
private const val PAYMENT_UNVERIFIED = "/pg/v4/payment/unVerified.json"
private const val PAYMENT_REVERSE = "/pg/v4/payment/reverse.json"

/**
* Generates the URL for creating a payment request.
*
* @param sandBox A boolean indicating whether to use the sandbox (test) environment.
* @return The full URL for the payment request.
*/
fun createPayment(sandBox: Boolean): String {
return (if (sandBox) BASE_URL_SANDBOX else BASE_URL) + PAYMENT
}
private fun baseUrl(sandbox: Boolean): String =
if (sandbox) BASE_URL_SANDBOX else BASE_URL

fun createPayment(sandbox: Boolean): String =
baseUrl(sandbox) + PAYMENT

/**
* Generates the URL for verifying a payment.
*
* @param sandBox A boolean indicating whether to use the sandbox (test) environment.
* @return The full URL for the payment verification request.
*/
fun paymentVerify(sandBox: Boolean): String {
return (if (sandBox) BASE_URL_SANDBOX else BASE_URL) + PAYMENT_VERIFY
}
fun paymentVerify(sandbox: Boolean): String =
baseUrl(sandbox) + PAYMENT_VERIFY

/**
* Generates the URL for inquiring about a payment.
*
* @param sandBox A boolean indicating whether to use the sandbox (test) environment.
* @return The full URL for the payment inquiry request.
*/
fun paymentInquiry(sandBox: Boolean): String {
return (if (sandBox) BASE_URL_SANDBOX else BASE_URL) + PAYMENT_INQUIRY
}
fun paymentInquiry(sandbox: Boolean): String =
baseUrl(sandbox) + PAYMENT_INQUIRY

/**
* Generates the URL for querying unverified payments.
*
* @param sandBox A boolean indicating whether to use the sandbox (test) environment.
* @return The full URL for the unverified payment query request.
*/
fun paymentUnVerified(sandBox: Boolean): String {
return (if (sandBox) BASE_URL_SANDBOX else BASE_URL) + PAYMENT_UN_VERIFIED
}
fun paymentUnverified(sandbox: Boolean): String =
baseUrl(sandbox) + PAYMENT_UNVERIFIED

/**
* Generates the URL for reversing a payment.
*
* @param sandBox A boolean indicating whether to use the sandbox (test) environment.
* @return The full URL for the payment reversal request.
*/
fun paymentReverse(sandBox: Boolean): String {
return (if (sandBox) BASE_URL_SANDBOX else BASE_URL) + PAYMENT_REVERSE
}
fun paymentReverse(sandbox: Boolean): String =
baseUrl(sandbox) + PAYMENT_REVERSE

/**
* Generates the URL for redirecting a user to the payment page with the provided authority.
*
* @param authority The payment authority returned by the payment gateway.
* @param sandBox A boolean indicating whether to use the sandbox (test) environment.
* @return The full URL for redirecting the user to the payment page.
*/
fun getRedirectUrl(authority: String, sandBox: Boolean): String {
if ((authority ?: "").isEmpty()) return ""
return (if (sandBox) BASE_URL_SANDBOX else BASE_URL) + START_PAY_URL + authority
}
}
fun getRedirectUrl(authority: String, sandbox: Boolean): String =
if (authority.isBlank()) "" else baseUrl(sandbox) + START_PAY_URL + authority
}
Loading