diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index f73c5b1..4eef085 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -48,14 +48,26 @@ jobs:
javadocJar sourcesJar \
--no-daemon --stacktrace
- # Sign and publish (no need to build again!)
+ # Import GPG key for signing
+ - name: Import GPG key
+ uses: crazy-max/ghaction-import-gpg@v6
+ with:
+ gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
+ passphrase: ${{ secrets.GPG_PASSPHRASE }}
+
+ # Sign and publish to Maven Central (Central Portal)
- name: Publish to Maven Central
env:
- ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_PRIVATE_KEY }}
- ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_PASSPHRASE }}
- ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.SONATYPE_USERNAME }}
- ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.SONATYPE_PASSWORD }}
+ ORG_GRADLE_PROJECT_centralPortalUsername: ${{ secrets.CENTRAL_PORTAL_USERNAME }}
+ ORG_GRADLE_PROJECT_centralPortalPassword: ${{ secrets.CENTRAL_PORTAL_PASSWORD }}
+ ORG_GRADLE_PROJECT_signing.keyId: ${{ secrets.SIGNING_KEY_ID }}
+ ORG_GRADLE_PROJECT_signing.password: ${{ secrets.GPG_PASSPHRASE }}
+ ORG_GRADLE_PROJECT_signing.secretKeyRingFile: ${{ github.workspace }}/.gnupg/secring.gpg
run: |
+ # Export secret key for Gradle signing
+ gpg --export-secret-keys > ${{ github.workspace }}/.gnupg/secring.gpg
+
+ # Publish to Maven Central via Central Portal
./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository \
--no-daemon --stacktrace
@@ -86,8 +98,11 @@ jobs:
### Changes
See [CHANGELOG.md](CHANGELOG.md) for details.
+
+ ---
+ **Note:** This is a pre-1.0.0 release. The API may change between releases.
draft: false
- prerelease: false
+ prerelease: ${{ startsWith(steps.extract_version.outputs.VERSION, '0.') }}
files: |
build/libs/*.jar
build/publications/maven/pom-default.xml
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bb96f0b..ea67e46 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,35 +8,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
-- Complete Open Payments API implementation with Java 25
-- GNAP (Grant Negotiation and Authorization Protocol) support
-- HTTP Message Signatures with Ed25519
-- Token lifecycle management (rotation and revocation)
-- HTTP interceptors for logging, authentication, and error handling
-- Async-first API with CompletableFuture
-- Immutable data models using Java records
-- Comprehensive JavaDoc documentation
-- 465 unit tests with 100% pass rate
-- PMD and Checkstyle quality checks
-- Automatic code formatting with Spotless
-- Complete resource service implementations:
- - WalletAddressService for wallet address discovery
- - IncomingPaymentService for receiving payments
- - OutgoingPaymentService for sending payments
- - QuoteService for exchange rate quotes
-- Comprehensive usage examples and documentation
+- 📦 **Maven Central Publishing Setup**
+ - Automated CI/CD publishing via GitHub Actions
+ - Central Portal integration
+ - Comprehensive release guide (RELEASE_GUIDE.md)
+ - GPG artifact signing configuration
+ - Automated GitHub Release creation
+ - JavaDoc deployment to GitHub Pages
+ - Publication verification in CI/CD pipeline
### Changed
-- Converted payment and auth domain models to Java records for improved immutability
- - IncomingPayment, OutgoingPayment, Quote (payment models)
- - AccessToken, Grant, AccessRight (auth models)
- - Preserved builder patterns for backward compatibility
- - Added Optional-returning getters for nullable fields
- - Maintained custom equals/hashCode/toString implementations
+- 🔢 **Versioning Strategy**
+ - Changed from `1.0.0-SNAPSHOT` to `0.1.0` (pre-1.0 development)
+ - Adopted semantic versioning with 0.x.y for initial development
+ - Configured for release-only versions (no SNAPSHOT support)
+
+- 🔧 **Publishing Configuration**
+ - Fixed Maven Central Portal URL to `https://central.sonatype.com`
+ - Updated to token-based authentication (Central Portal tokens)
+ - Removed legacy OSSRH references and configurations
+ - Removed duplicate version declarations in build files
+ - Simplified publishing workflow
+
+- 📝 **Documentation**
+ - Added publishing docs into single comprehensive RELEASE_GUIDE.md
+ - Updated CI_CD_SETUP.md to reflect Central Portal approach
+ - Updated GITHUB_ACTIONS_SETUP.md with correct secret names
+
+- ⚙️ **CI/CD Workflow**
+ - Updated release.yml for Central Portal authentication
+ - Added GPG key import step using crazy-max/ghaction-import-gpg
+ - Updated secret names: `CENTRAL_PORTAL_*` instead of `SONATYPE_*`
+ - Added automatic pre-release flag for 0.x versions
+ - Enhanced artifact verification steps
### Removed
-- Phase-specific TODO comments from completed implementation
-- Replaced with proper documentation for future enhancements
+- 🗑️ **Cleanup**
+ - Removed all SNAPSHOT version references from codebase
+ - Removed OSSRH (legacy Sonatype) documentation and references
+ - Removed duplicate/redundant publishing documentation files
+ - Removed old sunset `s01.oss.sonatype.org` endpoint references
## [0.1.0] - Initial Development
@@ -163,7 +174,6 @@ Not applicable for initial release.
1. Integration tests for Phases 5+ are still pending implementation
2. Performance benchmarks not yet established
-3. Maven Central publication pending first stable release
## Future Plans
@@ -173,17 +183,18 @@ Not applicable for initial release.
- Additional authentication schemes
- Enhanced error recovery
-### Version 1.0.0
-- Production-ready release
+### Version 1.0.0 (Stable Release)
+- Production-ready release with API stability commitment
- Full Open Payments API coverage
-- Performance benchmarks
-- Maven Central publication
+- Performance benchmarks and optimization
- Comprehensive integration testing
- Production deployment guide
+- First stable release on Maven Central
## Contributors
- Boniface Kabaso - Initial implementation
+- Espoir Diteekemena - Initial implementation and Documentation
## References
diff --git a/PROJECT_STATUS.md b/PROJECT_STATUS.md
index 196c857..7868434 100644
--- a/PROJECT_STATUS.md
+++ b/PROJECT_STATUS.md
@@ -356,7 +356,7 @@ All completed phases meet the following quality standards:
- **HTTP**: Apache HttpClient 5 (abstracted, multiple implementations)
- **JSON**: Jackson with Jdk8Module (for Optional support) and JSR310 (for Java Time)
- **Crypto**: Ed25519 (Java standard library KeyPairGenerator)
-- **Testing**: JUnit 5, Mockito, AssertJ
+- **Testing**: JUnit 6, Mockito, AssertJ
- **Quality**: PMD, Checkstyle, SpotBugs, Spotless
---
diff --git a/README.md b/README.md
index 3e61a43..73e46cc 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# Open Payments Java SDK
-[](https://github.com/bonifacekabaso/open-payments-java/actions/workflows/ci.yml)
-[](https://github.com/bonifacekabaso/open-payments-java/actions/workflows/ci.yml)
+[](https://github.com/boniface/open-payments-java/actions/workflows/ci.yml)
+[](https://github.com/boniface/open-payments-java/actions/workflows/ci.yml)
[](https://github.com/boniface/open-payments-java/actions/workflows/ci.yml)
[](https://openjdk.java.net/)
[](LICENSE)
@@ -41,7 +41,7 @@ graph TD
Auth ~~~ Note
Resource ~~~ Note
- Note["Operated by Account Service Entity (ASE)
(Bank, Wallet Provider, Payment Processor)"]
+ Note["Operated by Account Service Entity (ASE) (Bank, Wallet Provider, Payment Processor)"]
end
App -->|HTTP Request via SDK| SDK
@@ -80,7 +80,7 @@ graph TD
```kotlin
dependencies {
- implementation("zm.hashcode:open-payments-java:1.0-SNAPSHOT")
+ implementation("zm.hashcode:open-payments-java:0.1.0")
}
```
@@ -90,10 +90,12 @@ dependencies {
zm.hashcode
open-payments-java
- 1.0-SNAPSHOT
+ 0.1.0
```
+> **Note:** Versions 0.x.y are pre-1.0.0 releases. The API may change between releases until 1.0.0 is reached.
+
### Basic Usage
```java
@@ -213,10 +215,10 @@ cd open-payments-java
- Complete unit test implementations
- Performance optimization
-- Maven Central publication
+- Maven Central publication (see [MAVEN_CENTRAL_PUBLISHING.md](MAVEN_CENTRAL_PUBLISHING.md))
- Version 1.0 release
-**Version**: 0.1.0-SNAPSHOT | **Target Release**: 1.0.0 | **Java**: 25+
+**Version**: 1.0.0-SNAPSHOT | **Target Release**: 1.0.0 | **Java**: 25+
See [PROJECT_STATUS.md](PROJECT_STATUS.md) for detailed roadmap.
diff --git a/RELEASE_GUIDE.md b/RELEASE_GUIDE.md
new file mode 100644
index 0000000..bf1c242
--- /dev/null
+++ b/RELEASE_GUIDE.md
@@ -0,0 +1,586 @@
+# Maven Central Release Guide
+
+Complete guide for publishing `open-payments-java` to Maven Central using automated CI/CD and the Central Portal.
+
+---
+
+## Table of Contents
+
+- [Overview](#overview)
+- [Versioning Strategy](#versioning-strategy)
+- [Prerequisites (One-Time Setup)](#prerequisites-one-time-setup)
+- [Automated Release Process](#automated-release-process)
+- [Manual Release Process (Fallback)](#manual-release-process-fallback)
+- [Troubleshooting](#troubleshooting)
+- [CI/CD Integration](#cicd-integration)
+- [Quick Reference](#quick-reference)
+
+---
+
+## Overview
+
+This project is configured to publish to Maven Central via the **Central Portal** API:
+
+- **Publishing Endpoint:** `https://central.sonatype.com`
+- **Namespace:** `zm.hashcode`
+- **Artifact:** `open-payments-java`
+- **Group ID:** `zm.hashcode`
+- **Authentication:** Token-based
+- **Current Version:** `0.1.0` (pre-1.0 development)
+
+**Publication Methods:**
+1. **Automated (Recommended):** Push a git tag → GitHub Actions publishes automatically
+2. **Manual (Fallback):** Run Gradle commands locally
+
+---
+
+## Versioning Strategy
+
+We follow [Semantic Versioning 2.0.0](https://semver.org/) with a **pre-1.0.0 development phase**.
+
+### Version Number Format: `MAJOR.MINOR.PATCH`
+
+#### Pre-1.0 Development (Current)
+- **0.x.y** = Initial development (API may change)
+ - `0.1.0` = First release ← **YOU ARE HERE**
+ - `0.2.0` = New features added
+ - `0.3.0` = More features, bug fixes
+ - Continue until API is stable...
+
+#### Post-1.0 Stable
+- **1.0.0** = First stable, production-ready release
+ - Signals API stability commitment
+ - Breaking changes require MAJOR version bump
+
+- **1.x.y+** = Production releases
+ - `1.1.0` = New backward-compatible features (MINOR)
+ - `1.0.1` = Backward-compatible bug fixes (PATCH)
+ - `2.0.0` = Breaking changes (MAJOR)
+
+### When to Bump Versions
+
+| Change Type | Current Phase (0.x) | Stable Phase (1.x+) | Example |
+|-------------|---------------------|---------------------|---------|
+| Breaking API changes | MINOR | MAJOR | 0.1.0 → 0.2.0 or 1.0.0 → 2.0.0 |
+| New features | MINOR | MINOR | 0.2.0 → 0.3.0 or 1.0.0 → 1.1.0 |
+| Bug fixes | PATCH | PATCH | 0.2.0 → 0.2.1 or 1.0.1 → 1.0.2 |
+| API stabilization | MAJOR (→ 1.0.0) | N/A | 0.9.0 → 1.0.0 |
+
+---
+
+## Prerequisites (One-Time Setup)
+
+### 1. Central Portal Account
+
+**Register at:** https://central.sonatype.com
+
+- Sign up with your GitHub account
+- Verify your email address
+- **Note:** The `zm.hashcode` namespace is already verified and ready to use ✓
+
+### 2. Generate Publishing Token
+
+1. Go to https://central.sonatype.com/account
+2. Click "**Generate User Token**"
+3. Copy both the **username** and **password**
+
+### 3. GPG Signing Setup
+
+All artifacts published to Maven Central must be cryptographically signed with GPG.
+
+#### Generate GPG Key
+
+```bash
+# Generate new GPG key (use default options)
+gpg --gen-key
+
+# Follow prompts:
+# - Real name:
+# - Email:
+# - Passphrase: (choose a strong password - save it!)
+```
+
+#### List Keys and Get KEY_ID
+
+```bash
+# List your keys to get KEY_ID
+gpg --list-keys
+
+# Example output:
+# pub rsa3072 2025-01-15 [SC] [expires: 2027-01-15]
+# ABCD1234EFGH5678IJKL9012MNOP3456QRST7890 ← This is the KEY_ID
+# uid [ultimate] FULL NAME
+
+# The KEY_ID is the full 40-character fingerprint
+# The SIGNING_KEY_ID is the last 8 characters (e.g., QRST7890)
+```
+
+#### Export Public Key to Key Servers (REQUIRED)
+
+```bash
+# Export to multiple key servers (for redundancy)
+gpg --keyserver keys.openpgp.org --send-keys ABCD1234EFGH5678IJKL9012MNOP3456QRST7890
+gpg --keyserver keyserver.ubuntu.com --send-keys ABCD1234EFGH5678IJKL9012MNOP3456QRST7890
+
+# Verify it was uploaded (wait a few minutes)
+gpg --keyserver keys.openpgp.org --recv-keys ABCD1234EFGH5678IJKL9012MNOP3456QRST7890
+```
+
+### 4. Configure GitHub Secrets
+
+**For Automated Releases (Recommended)**
+
+Go to: `https://github.com/boniface/open-payments-java/settings/secrets/actions`
+
+Add these **5 required secrets**:
+
+| Secret Name | Value | How to Get |
+|-------------|-------|------------|
+| `GPG_PRIVATE_KEY` | ASCII armored GPG private key | `gpg --export-secret-keys --armor YOUR_KEY_ID` |
+| `GPG_PASSPHRASE` | Your GPG key passphrase | The password you chose during `gpg --gen-key` |
+| `SIGNING_KEY_ID` | Last 8 characters of GPG key ID | From `gpg --list-keys` (e.g., `QRST7890`) |
+| `CENTRAL_PORTAL_USERNAME` | Central Portal token username | From step 2 above |
+| `CENTRAL_PORTAL_PASSWORD` | Central Portal token password | From step 2 above |
+
+**Example: Getting GPG_PRIVATE_KEY**
+
+```bash
+# Export private key in ASCII armor format
+gpg --export-secret-keys --armor ABCD1234EFGH5678IJKL9012MNOP3456QRST7890
+
+# Copy the ENTIRE output (including BEGIN/END lines):
+# -----BEGIN PGP PRIVATE KEY BLOCK-----
+# ...
+# -----END PGP PRIVATE KEY BLOCK-----
+```
+
+### 5. Configure Local Credentials (For Manual Releases)
+
+**Option A: User-level configuration** (recommended - keeps secrets out of repo)
+
+Create/edit `~/.gradle/gradle.properties`:
+
+```properties
+# GPG Signing
+signing.keyId=QRST7890
+signing.password=your-gpg-passphrase
+signing.secretKeyRingFile=/[HOME DIRECTORY]/.gnupg/secring.gpg
+
+# Central Portal Authentication
+centralPortalUsername=your-token-username
+centralPortalPassword=your-token-password
+```
+
+**Export Secret Key for Gradle (GPG 2.1+)**
+
+```bash
+# Export to legacy format for Gradle
+gpg --export-secret-keys > ~/.gnupg/secring.gpg
+```
+
+---
+
+## Automated Release Process
+
+### Overview
+
+Push a git tag → GitHub Actions automatically publishes to Maven Central.
+
+
+### Step 1: Update Version
+
+Edit `gradle.properties`:
+
+```properties
+# For first release (already set):
+version=0.1.0
+
+# For subsequent releases:
+version=0.2.0 # New features
+version=0.1.1 # Bug fix
+version=1.0.0 # Stable release
+```
+
+### Step 2: Update CHANGELOG (Optional but Recommended)
+
+Document what changed in this release:
+
+```markdown
+## [0.1.0] - 2025-01-22
+
+### Added
+- Initial implementation of Open Payments client
+- Support for wallet addresses, quotes, and payments
+- GNAP authorization flow
+- HTTP signature authentication
+
+### Fixed
+- None (first release)
+
+### Changed
+- None (first release)
+```
+
+### Step 3: Run Tests Locally
+
+```bash
+# Run all tests
+./gradlew test
+
+# Build artifacts
+./gradlew build
+
+# Verify coverage
+./gradlew jacocoTestCoverageVerification
+
+# Check for PMD violations
+./gradlew pmdMain
+```
+
+### Step 4: Commit and Tag
+
+```bash
+# Commit version change
+git add gradle.properties CHANGELOG.md
+git commit -m "Release version 0.1.0"
+
+# Create annotated tag (version must match gradle.properties)
+git tag -a v0.1.0 -m "Release version 0.1.0"
+
+# Push commits and tag to trigger release
+git push origin feature/maven-publish
+git push origin v0.1.0
+```
+
+### Step 5: Monitor the Release
+
+1. **GitHub Actions:** Watch the workflow run
+ ```
+ https://github.com/boniface/open-payments-java/actions
+ ```
+
+2. **Central Portal:** Check upload status (after ~5-10 minutes)
+ ```
+ https://central.sonatype.com/publishing
+ ```
+
+3. **Maven Central:** Verify publication (after ~15-30 minutes)
+ ```
+ https://central.sonatype.com/artifact/zm.hashcode/open-payments-java
+ https://search.maven.org/artifact/zm.hashcode/open-payments-java
+ ```
+
+### Step 6: Verify Publication
+
+```bash
+# Check if artifact is available (wait 15-30 minutes after release)
+curl -I "https://repo1.maven.org/maven2/zm/hashcode/open-payments-java/0.1.0/open-payments-java-0.1.0.pom"
+
+# Should return: HTTP/1.1 200 OK
+```
+
+### Step 7: Post-Release
+
+#### Update to Next Version
+
+Edit `gradle.properties`:
+
+```properties
+# Bump to next version
+version=0.2.0
+```
+
+```bash
+git add gradle.properties
+git commit -m "Bump version to 0.2.0"
+git push origin feature/maven-publish
+```
+
+#### Test Installation
+
+Create a test project to verify:
+
+```kotlin
+// build.gradle.kts
+dependencies {
+ implementation("zm.hashcode:open-payments-java:0.1.0")
+}
+```
+
+```bash
+./gradlew build --refresh-dependencies
+```
+
+---
+
+## Manual Release Process (Fallback)
+
+If CI/CD fails or you need to publish manually:
+
+### Step 1: Prepare Release
+
+```bash
+# Update version in gradle.properties
+version=0.1.0
+
+# Commit and tag
+git add gradle.properties
+git commit -m "Release version 0.1.0"
+git tag -a v0.1.0 -m "Release version 0.1.0"
+git push origin main
+git push origin v0.1.0
+```
+
+### Step 2: Build and Test
+
+```bash
+# Clean build
+./gradlew clean
+
+# Run all tests
+./gradlew test
+
+# Build all artifacts (main JAR, sources JAR, javadoc JAR)
+./gradlew build
+
+# Verify artifacts were created
+ls -lh build/libs/
+# Should see:
+# - open-payments-java-0.1.0.jar
+# - open-payments-java-0.1.0-sources.jar
+# - open-payments-java-0.1.0-javadoc.jar
+```
+
+### Step 3: Publish to Central Portal
+
+Ensure you have configured `~/.gradle/gradle.properties` with credentials (see Prerequisites).
+
+```bash
+# Publish to Maven Central (signs and uploads)
+./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository
+
+# Or in separate steps for more control:
+./gradlew publishToSonatype # Upload to staging
+./gradlew closeAndReleaseSonatypeStagingRepository # Release to Central
+```
+
+**What happens:**
+1. Gradle builds all artifacts (JAR, sources, javadoc)
+2. Signs each artifact with your GPG key
+3. Uploads to Central Portal staging repository
+4. Validates artifacts (POM, signatures, required files)
+5. Releases to Maven Central (public within 15-30 minutes)
+
+### Step 4: Create GitHub Release (Manual)
+
+```bash
+# Via GitHub CLI
+gh release create v0.1.0 \
+ --title "Release 0.1.0" \
+ --notes "Initial release of Open Payments Java SDK" \
+ build/libs/open-payments-java-0.1.0.jar \
+ build/libs/open-payments-java-0.1.0-sources.jar \
+ build/libs/open-payments-java-0.1.0-javadoc.jar
+
+# Or via web interface:
+# https://github.com/boniface/open-payments-java/releases/new
+```
+
+---
+
+## Troubleshooting
+
+### ❌ "No value has been specified for property 'signing.keyId'"
+
+**Problem:** GPG signing not configured
+
+**Solution:**
+```bash
+# Configure in ~/.gradle/gradle.properties
+signing.keyId=QRST7890
+signing.password=your-gpg-passphrase
+signing.secretKeyRingFile=/Users/yourusername/.gnupg/secring.gpg
+```
+
+### ❌ "Unable to find secret key"
+
+**Problem:** Secret keyring not found
+
+**Solution:**
+```bash
+# Export secret key to legacy format
+gpg --export-secret-keys > ~/.gnupg/secring.gpg
+
+# Verify location
+ls -lh ~/.gnupg/secring.gpg
+```
+
+### ❌ Publishing Fails with "401 Unauthorized"
+
+**Problem:** Invalid or expired Central Portal token
+
+**Solution:**
+1. Go to https://central.sonatype.com/account
+2. Click "Generate User Token" (revokes old token)
+3. Update credentials:
+ - **Local:** Update `~/.gradle/gradle.properties`
+ - **CI/CD:** Update GitHub secrets `CENTRAL_PORTAL_USERNAME` and `CENTRAL_PORTAL_PASSWORD`
+
+### ❌ Signature Verification Fails
+
+**Problem:** GPG public key not on key servers
+
+**Solution:**
+```bash
+# Re-upload to key servers
+gpg --keyserver keys.openpgp.org --send-keys YOUR_KEY_ID
+gpg --keyserver keyserver.ubuntu.com --send-keys YOUR_KEY_ID
+
+# Wait a few minutes, then verify
+gpg --keyserver keys.openpgp.org --recv-keys YOUR_KEY_ID
+```
+
+### ❌ Artifacts Not Appearing on Maven Central
+
+**Problem:** Artifact not showing after 30+ minutes
+
+**Solution:**
+1. Check Central Portal status: https://central.sonatype.com/publishing
+2. Look for validation errors in the publishing log
+3. Check direct repository: `https://repo1.maven.org/maven2/zm/hashcode/open-payments-java/`
+4. Contact Central Portal support if issue persists: https://central.sonatype.com/support
+
+### ❌ Build Fails in CI/CD
+
+**Problem:** Tests fail or build errors in GitHub Actions
+
+**Solution:**
+```bash
+# Run locally to debug
+./gradlew clean test build --stacktrace
+
+# Fix issues, commit, and re-tag:
+git tag -d v0.1.0 # Delete local tag
+git push origin :refs/tags/v0.1.0 # Delete remote tag
+
+# After fixes:
+git tag -a v0.1.0 -m "Release version 0.1.0"
+git push origin v0.1.0
+```
+
+---
+
+## CI/CD Integration
+
+### GitHub Actions Workflow
+
+The release workflow (`.github/workflows/release.yml`) is triggered when you push a git tag matching `v*.*.*`.
+
+**What it does:**
+1. ✅ Validates Gradle wrapper
+2. ✅ Runs all quality checks (tests, coverage, PMD, SpotBugs)
+3. ✅ Builds all artifacts (JAR, sources, javadoc)
+4. ✅ Imports GPG key for signing
+5. ✅ Signs artifacts with GPG
+6. ✅ Publishes to Maven Central via Central Portal
+7. ✅ Creates GitHub Release with artifacts
+8. ✅ Deploys JavaDoc to GitHub Pages
+9. ✅ Verifies artifact availability on Maven Central
+
+**Required GitHub Secrets:**
+- `GPG_PRIVATE_KEY` - ASCII armored GPG private key
+- `GPG_PASSPHRASE` - GPG key passphrase
+- `SIGNING_KEY_ID` - Last 8 characters of GPG key ID
+- `CENTRAL_PORTAL_USERNAME` - Central Portal token username
+- `CENTRAL_PORTAL_PASSWORD` - Central Portal token password
+
+---
+
+## Quick Reference
+
+### Release Checklist
+
+- [ ] All tests passing: `./gradlew test`
+- [ ] Code formatted: `./gradlew spotlessApply`
+- [ ] No PMD violations: `./gradlew pmdMain`
+- [ ] Coverage meets threshold: `./gradlew jacocoTestCoverageVerification`
+- [ ] CHANGELOG.md updated
+- [ ] Version bumped in `gradle.properties`
+- [ ] Committed changes: `git commit -m "Release version X.Y.Z"`
+- [ ] Created git tag: `git tag -a vX.Y.Z -m "Release version X.Y.Z"`
+- [ ] Pushed to remote: `git push origin branch && git push origin vX.Y.Z`
+- [ ] GitHub Actions workflow succeeded
+- [ ] Verified on Central Portal
+- [ ] Verified on Maven Central
+- [ ] Tested installation in sample project
+- [ ] Version bumped to next development version
+
+### Common Commands
+
+```bash
+# Automated Release
+git add gradle.properties CHANGELOG.md
+git commit -m "Release version 0.1.0"
+git tag -a v0.1.0 -m "Release version 0.1.0"
+git push origin feature/maven-publish && git push origin v0.1.0
+
+# Manual Release
+./gradlew clean test build
+./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository
+
+# Verify Publication
+curl -I "https://repo1.maven.org/maven2/zm/hashcode/open-payments-java/0.1.0/open-payments-java-0.1.0.pom"
+
+# Monitor Release
+open https://github.com/boniface/open-payments-java/actions
+open https://central.sonatype.com/publishing
+open https://central.sonatype.com/artifact/zm.hashcode/open-payments-java
+```
+
+### Version Examples
+
+```properties
+# Pre-1.0 releases
+version=0.1.0 # First release
+version=0.2.0 # New features
+version=0.1.1 # Bug fix
+version=1.0.0 # API stabilization
+
+# Post-1.0 releases
+version=1.1.0 # New features
+version=1.0.1 # Bug fix
+version=2.0.0 # Breaking changes
+```
+
+### Important Links
+
+| Resource | URL |
+|----------|-----|
+| **Central Portal** | https://central.sonatype.com |
+| **Account/Tokens** | https://central.sonatype.com/account |
+| **Publishing Status** | https://central.sonatype.com/publishing |
+| **Namespace Management** | https://central.sonatype.com/publishing/namespaces |
+| **Maven Central Search** | https://search.maven.org/artifact/zm.hashcode/open-payments-java |
+| **Direct Repository** | https://repo1.maven.org/maven2/zm/hashcode/open-payments-java/ |
+| **GitHub Actions** | https://github.com/boniface/open-payments-java/actions |
+| **GitHub Secrets** | https://github.com/boniface/open-payments-java/settings/secrets/actions |
+| **Central Portal Docs** | https://central.sonatype.org/publish/publish-portal-gradle/ |
+| **GPG Guide** | https://central.sonatype.org/publish/requirements/gpg/ |
+| **Support** | https://central.sonatype.com/support |
+
+---
+
+## Resources
+
+- **Central Portal Documentation:** https://central.sonatype.org/publish/publish-portal-gradle/
+- **Gradle Nexus Publish Plugin:** https://github.com/gradle-nexus/publish-plugin
+- **GPG Signing Guide:** https://central.sonatype.org/publish/requirements/gpg/
+- **Semantic Versioning:** https://semver.org/
+- **Project Issues:** https://github.com/boniface/open-payments-java/issues
+
+---
+
+**Last Updated:** 2025-10-22
+**Publishing Method:** Maven Central Portal (current standard)
+**Namespace:** `zm.hashcode` (verified ✓)
+**Current Version:** `0.1.0`
diff --git a/build.gradle.kts b/build.gradle.kts
index ea7f259..b33c7bb 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -16,8 +16,9 @@ plugins {
id("utilities-convention")
}
-group = "zm.hashcode"
-version = "1.0-SNAPSHOT"
+// Group and version are defined in gradle.properties
+// group = zm.hashcode
+// version = 1.0.0-SNAPSHOT
java {
toolchain {
diff --git a/buildSrc/src/main/kotlin/publishing-convention.gradle.kts b/buildSrc/src/main/kotlin/publishing-convention.gradle.kts
index 29a796f..ee1ab62 100644
--- a/buildSrc/src/main/kotlin/publishing-convention.gradle.kts
+++ b/buildSrc/src/main/kotlin/publishing-convention.gradle.kts
@@ -1,3 +1,5 @@
+import java.time.Duration
+
plugins {
`java-library`
`maven-publish`
@@ -31,12 +33,12 @@ publishing {
developer {
id = "boniface"
name = "Boniface Kabaso"
- email = "boniface.kabaso@example.com"
+ email = "550236+boniface@users.noreply.github.com"
}
developer {
id = "espoir"
- name = "Espoir D"
- email = "espoir.d@example.com"
+ name = "Espoir Diteekemena"
+ email = "47171587+ESPOIR-DITE@users.noreply.github.com"
}
}
@@ -59,8 +61,21 @@ signing {
nexusPublishing {
repositories {
sonatype {
- nexusUrl = uri("https://s01.oss.sonatype.org/service/local/")
- snapshotRepositoryUrl = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")
+ // Maven Central Portal
+ nexusUrl = uri("https://central.sonatype.com")
+
+ // Use Central Portal token authentication
+ // Set via environment variables or ~/.gradle/gradle.properties:
+ // centralPortalUsername=
+ // centralPortalPassword=
+ username = project.findProperty("centralPortalUsername") as String?
+ ?: System.getenv("CENTRAL_PORTAL_USERNAME")
+ password = project.findProperty("centralPortalPassword") as String?
+ ?: System.getenv("CENTRAL_PORTAL_PASSWORD")
}
}
+
+ // Timeout configuration for large uploads
+ connectTimeout = Duration.ofMinutes(3)
+ clientTimeout = Duration.ofMinutes(3)
}
diff --git a/docs/CI_CD_SETUP.md b/docs/CI_CD_SETUP.md
index ab08f51..abd3eba 100644
--- a/docs/CI_CD_SETUP.md
+++ b/docs/CI_CD_SETUP.md
@@ -8,7 +8,7 @@ This document describes the CI/CD pipeline configuration for the Open Payments J
## Overview
-The project uses GitHub Actions for continuous integration, quality checks, and automated releases to Maven Central.
+The project uses GitHub Actions for continuous integration, quality checks, and automated releases to Maven Central via the **Central Portal**.
## Workflows
@@ -39,13 +39,13 @@ Runs on every push and pull request to `main` and `develop` branches.
### 2. Release Workflow (`.github/workflows/release.yml`)
-Triggers when a version tag is pushed (e.g., `v1.0.0`).
+Triggers when a version tag is pushed (e.g., `v0.1.0`).
#### Steps
1. **Validation** - Gradle wrapper validation
2. **Quality Gates** - All CI checks must pass
3. **Build & Sign** - Artifacts signed with GPG
-4. **Publish to Maven Central** - Via Sonatype OSSRH
+4. **Publish to Maven Central** - Via Central Portal
5. **GitHub Release** - Create release with artifacts
6. **JavaDoc Deployment** - Publish to GitHub Pages
7. **Verification** - Confirm artifact availability on Maven Central
@@ -64,10 +64,11 @@ Analyzes code for security vulnerabilities and quality issues.
Configure these secrets in your GitHub repository settings:
### Maven Central Publishing
-- `SONATYPE_USERNAME` - Sonatype JIRA username
-- `SONATYPE_PASSWORD` - Sonatype JIRA password
-- `GPG_PRIVATE_KEY` - Base64 encoded GPG private key
+- `CENTRAL_PORTAL_USERNAME` - Central Portal token username
+- `CENTRAL_PORTAL_PASSWORD` - Central Portal token password
+- `GPG_PRIVATE_KEY` - ASCII armored GPG private key
- `GPG_PASSPHRASE` - GPG key passphrase
+- `SIGNING_KEY_ID` - Last 8 characters of GPG key ID
### Code Coverage
- `CODECOV_TOKEN` - Codecov.io token for coverage reports
@@ -136,12 +137,17 @@ open build/reports/jacoco/test/html/index.html
### Prerequisites
-1. **Sonatype JIRA Account**
- - Create account at https://issues.sonatype.org
- - Request new project (OSSRH ticket)
- - Wait for approval (~2 business days)
+1. **Central Portal Account**
+ - Register at https://central.sonatype.com
+ - Sign up with GitHub (recommended)
+ - **Note:** The `zm.hashcode` namespace is already verified
-2. **GPG Key for Signing**
+2. **Central Portal Token**
+ - Go to https://central.sonatype.com/account
+ - Click "Generate User Token"
+ - Save username and password as GitHub secrets
+
+3. **GPG Key for Signing**
```bash
# Generate GPG key
gpg --gen-key
@@ -149,49 +155,50 @@ open build/reports/jacoco/test/html/index.html
# List keys
gpg --list-keys
- # Export private key (base64)
- gpg --export-secret-keys YOUR_KEY_ID | base64
+ # Export private key (ASCII armored)
+ gpg --export-secret-keys --armor YOUR_KEY_ID
- # Export public key to keyserver
+ # Export public key to keyservers
+ gpg --keyserver keys.openpgp.org --send-keys YOUR_KEY_ID
gpg --keyserver keyserver.ubuntu.com --send-keys YOUR_KEY_ID
```
-3. **GitHub Secrets Configuration**
+4. **GitHub Secrets Configuration**
- Add all required secrets to repository
- - Test with a snapshot release first
+ - See [RELEASE_GUIDE.md](../RELEASE_GUIDE.md) for detailed setup
### Release Process
1. **Prepare Release**
```bash
- # Update version in build.gradle.kts
- version = "1.0.0"
+ # Update version in gradle.properties
+ version=0.1.0
# Commit version change
- git add build.gradle.kts
- git commit -m "chore: prepare release 1.0.0"
+ git add gradle.properties
+ git commit -m "Release version 0.1.0"
git push
```
2. **Create Release Tag**
```bash
# Create and push tag
- git tag -a v1.0.0 -m "Release version 1.0.0"
- git push origin v1.0.0
+ git tag -a v0.1.0 -m "Release version 0.1.0"
+ git push origin v0.1.0
```
3. **Monitor Workflow**
- Watch GitHub Actions for progress
- Release workflow publishes to Maven Central
- - Verify artifact on https://repo1.maven.org/maven2/
+ - Verify artifact on https://central.sonatype.com/artifact/zm.hashcode/open-payments-java
4. **Post-Release**
```bash
- # Update to next snapshot version
- version = "1.1.0-SNAPSHOT"
+ # Update to next version
+ version=0.2.0
- git add build.gradle.kts
- git commit -m "chore: prepare for next development iteration"
+ git add gradle.properties
+ git commit -m "Bump version to 0.2.0"
git push
```
@@ -201,7 +208,7 @@ Update README badges with actual values once integrated:
```markdown
[](https://github.com/boniface/open-payments-java/actions)
-[](https://codecov.io/gh/boniface/open-payments-java)
+[](https://codecov.io/gh/boniface/open-payments-java)
[](https://sonarcloud.io/dashboard?id=boniface_open-payments-java)
[](https://search.maven.org/artifact/zm.hashcode/open-payments-java)
```
@@ -226,22 +233,22 @@ open build/reports/jacoco/test/html/index.html
# Commit formatted code
git add .
-git commit -m "style: apply code formatting"
+git commit -m "Apply code formatting"
```
### Signing Fails
```bash
# Verify GPG key is configured
-echo $GPG_PRIVATE_KEY | base64 -d | gpg --import
+echo $GPG_PRIVATE_KEY | gpg --import
# Test signing locally
./gradlew signMavenJavaPublication
```
### Maven Central Sync Issues
-- Wait 10-30 minutes after release
+- Wait 15-30 minutes after release
- Check https://repo1.maven.org/maven2/zm/hashcode/open-payments-java/
-- Verify in Sonatype Nexus: https://s01.oss.sonatype.org/
+- Verify in Central Portal: https://central.sonatype.com/publishing
## Best Practices
@@ -263,13 +270,15 @@ Follow [Conventional Commits](https://www.conventionalcommits.org/):
### Versioning
Follow [Semantic Versioning](https://semver.org/):
-- MAJOR: Breaking changes
-- MINOR: New features (backward compatible)
-- PATCH: Bug fixes (backward compatible)
+- **0.x.y** - Pre-1.0 development (API may change)
+- **1.0.0** - First stable release
+- **MAJOR** - Breaking changes
+- **MINOR** - New features (backward compatible)
+- **PATCH** - Bug fixes (backward compatible)
## Resources
-- [Maven Central Publishing Guide](https://central.sonatype.org/publish/publish-guide/)
+- [Maven Central Portal Documentation](https://central.sonatype.org/publish/publish-portal-gradle/)
- [GitHub Actions Documentation](https://docs.github.com/en/actions)
- [JaCoCo Documentation](https://www.jacoco.org/jacoco/trunk/doc/)
- [SpotBugs Manual](https://spotbugs.readthedocs.io/)
diff --git a/docs/GITHUB_ACTIONS_SETUP.md b/docs/GITHUB_ACTIONS_SETUP.md
index 27161f3..6c4f6e5 100644
--- a/docs/GITHUB_ACTIONS_SETUP.md
+++ b/docs/GITHUB_ACTIONS_SETUP.md
@@ -2,7 +2,7 @@
## What's Been Configured
-This project has CI/CD following Maven Central.
+This project has automated CI/CD following Maven Central Portal best practices.
## Workflows Created
@@ -25,12 +25,12 @@ Runs on every push and PR to `main`/`develop`
- Java 25
### 2. **Release Workflow** (`.github/workflows/release.yml`)
-Triggers on version tags (e.g., `v1.0.0`)
+Triggers on version tags (e.g., `v0.1.0`)
**Steps:**
- Run all quality checks
- Build and sign artifacts (GPG)
-- Publish to Maven Central (Sonatype OSSRH)
+- Publish to Maven Central (Central Portal)
- Create GitHub Release
- Deploy JavaDoc to GitHub Pages
- Verify Maven Central availability
@@ -79,35 +79,41 @@ Configure these in **GitHub Settings → Secrets and variables → Actions**:
### Maven Central Publishing
```
-SONATYPE_USERNAME - Your Sonatype JIRA username
-SONATYPE_PASSWORD - Your Sonatype JIRA password
-GPG_PRIVATE_KEY - Base64 encoded GPG private key
-GPG_PASSPHRASE - Your GPG key passphrase
+CENTRAL_PORTAL_USERNAME - Central Portal token username
+CENTRAL_PORTAL_PASSWORD - Central Portal token password
+GPG_PRIVATE_KEY - ASCII armored GPG private key
+GPG_PASSPHRASE - Your GPG key passphrase
+SIGNING_KEY_ID - Last 8 characters of GPG key ID
```
### Code Coverage
```
-CODECOV_TOKEN - Token from codecov.io
+CODECOV_TOKEN - Token from codecov.io
```
### Code Quality
```
-SONAR_TOKEN - Token from sonarcloud.io
+SONAR_TOKEN - Token from sonarcloud.io
```
## Setup Checklist
### Before First Release
-- [ ] **Create Sonatype JIRA account**
- - Register at https://issues.sonatype.org
- - Create New Project ticket for `zm.hashcode`
- - Wait for approval (~2 business days)
+- [ ] **Create Central Portal account**
+ - Register at https://central.sonatype.com
+ - **Note:** `zm.hashcode` namespace is already verified ✓
+
+- [ ] **Generate Central Portal token**
+ - Go to https://central.sonatype.com/account
+ - Click "Generate User Token"
+ - Save username and password
- [ ] **Generate GPG key**
```bash
gpg --gen-key
- gpg --export-secret-keys YOUR_KEY_ID | base64 > private-key.txt
+ gpg --export-secret-keys --armor YOUR_KEY_ID
+ gpg --keyserver keys.openpgp.org --send-keys YOUR_KEY_ID
gpg --keyserver keyserver.ubuntu.com --send-keys YOUR_KEY_ID
```
@@ -123,8 +129,8 @@ SONAR_TOKEN - Token from sonarcloud.io
- Update `sonar.projectKey` and `sonar.organization` in build.gradle.kts
- [ ] **Configure GitHub Secrets**
- - Add all 6 secrets listed above
- - Test with a snapshot build first
+ - Add all 7 secrets listed above
+ - See [RELEASE_GUIDE.md](../RELEASE_GUIDE.md) for details
- [ ] **Update Repository Settings**
- Enable GitHub Pages (Settings → Pages → Source: gh-pages branch)
@@ -136,9 +142,9 @@ SONAR_TOKEN - Token from sonarcloud.io
Replace placeholders with actual values:
```markdown
-[](https://github.com/YOUR_USERNAME/open-payments-java/actions)
-[](https://codecov.io/gh/YOUR_USERNAME/open-payments-java)
-[](https://sonarcloud.io/dashboard?id=YOUR_ORG_open-payments-java)
+[](https://github.com/boniface/open-payments-java/actions)
+[](https://codecov.io/gh/boniface/open-payments-java)
+[](https://sonarcloud.io/dashboard?id=boniface_open-payments-java)
[](https://search.maven.org/artifact/zm.hashcode/open-payments-java)
```
@@ -146,32 +152,32 @@ Replace placeholders with actual values:
### 1. Prepare Release
```bash
-# Update version in build.gradle.kts
-version = "1.0.0"
+# Update version in gradle.properties
+version=0.1.0
-git add build.gradle.kts
-git commit -m "chore: prepare release 1.0.0"
+git add gradle.properties
+git commit -m "Release version 0.1.0"
git push
```
### 2. Create Tag
```bash
-git tag -a v1.0.0 -m "Release version 1.0.0"
-git push origin v1.0.0
+git tag -a v0.1.0 -m "Release version 0.1.0"
+git push origin v0.1.0
```
### 3. Monitor
- Watch GitHub Actions tab
- Release workflow runs automatically
-- Artifact published to Maven Central in ~10-30 minutes
+- Artifact published to Maven Central in ~15-30 minutes
### 4. Post-Release
```bash
-# Bump to next snapshot version
-version = "1.1.0-SNAPSHOT"
+# Bump to next version
+version=0.2.0
-git add build.gradle.kts
-git commit -m "chore: prepare for next development iteration"
+git add gradle.properties
+git commit -m "Bump version to 0.2.0"
git push
```
@@ -214,7 +220,7 @@ After setup, you'll have:
## 📚 Documentation
-See [docs/CI_CD_SETUP.md](docs/CI_CD_SETUP.md) for detailed documentation.
+See [docs/CI_CD_SETUP.md](CI_CD_SETUP.md) for detailed documentation.
## What Gets Checked on Every PR
diff --git a/docs/SDK_STRUCTURE.md b/docs/SDK_STRUCTURE.md
index 6a2ccb8..8def4d1 100644
--- a/docs/SDK_STRUCTURE.md
+++ b/docs/SDK_STRUCTURE.md
@@ -14,11 +14,24 @@ This document provides a detailed breakdown of the package structure and file or
zm.hashcode.openpayments/
├── client/ # Main client API
├── auth/ # Authentication & Authorization (GNAP)
+│ ├── exception/ # Auth-specific exceptions
+│ ├── grant/ # GNAP grant protocol
+│ ├── keys/ # Client key management
+│ ├── signature/ # HTTP message signatures
+│ └── token/ # Token management
├── wallet/ # Wallet Address operations
├── payment/ # Payment operations
│ ├── incoming/ # Incoming payments
│ ├── outgoing/ # Outgoing payments
│ └── quote/ # Payment quotes
+├── http/ # HTTP abstraction layer
+│ ├── config/ # HTTP client configuration
+│ ├── core/ # Core HTTP interfaces
+│ ├── factory/ # HTTP client factory
+│ ├── impl/ # HTTP client implementations
+│ ├── interceptor/ # Request/response interceptors
+│ └── resilience/ # Retry and resilience
+├── util/ # Cross-cutting utilities
└── model/ # Shared models and exceptions
```
diff --git a/gradle.properties b/gradle.properties
index b6b19e4..a4d464a 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,6 +1,21 @@
# Project
group=zm.hashcode
-version=1.0.0-SNAPSHOT
+version=0.1.0
+
+# Versioning Strategy (Semantic Versioning 2.0.0):
+# - 0.x.y = Initial development (pre-production, API may change)
+# - 0.1.0 = First release
+# - 0.2.0, 0.3.0 = Subsequent releases with new features/fixes
+# - 1.0.0 = First stable, production-ready release
+#
+# Publishing Notes:
+# - Central Portal does NOT support SNAPSHOT deployments
+# - Version is automatically updated by CI/CD after each release
+# - Manual releases: Update version here, create git tag, push
+
+# Kotlin
+kotlin.version=2.2.20
+kotlin.code.style=official
# Kotlin
kotlin.version=2.2.20
@@ -15,9 +30,16 @@ org.gradle.configuration-cache=true
# Java
kotlin.stdlib.default.dependency=false
-# Maven Central Publishing
-# signing.keyId=YOUR_KEY_ID
-# signing.password=YOUR_KEY_PASSWORD
-# signing.secretKeyRingFile=/path/to/secring.gpg
-# ossrhUsername=YOUR_SONATYPE_USERNAME
-# ossrhPassword=YOUR_SONATYPE_PASSWORD
\ No newline at end of file
+# Maven Central Publishing (Central Portal)
+# ================================================================
+# Configure in ~/.gradle/gradle.properties (NOT in project files)
+#
+# GPG Signing:
+# signing.keyId=YOUR_GPG_KEY_ID
+# signing.password=YOUR_GPG_KEY_PASSWORD
+# signing.secretKeyRingFile=/path/to/secring.gpg
+#
+# Central Portal Token (get from https://central.sonatype.com/account):
+# centralPortalUsername=YOUR_TOKEN_USERNAME
+# centralPortalPassword=YOUR_TOKEN_PASSWORD
+# ================================================================
\ No newline at end of file